

スクリプトを`/etc/rc.local`に書いた場合とそうでない場合で`-e`オプションの振舞いが異なるように見えるのですが, なぜでしょうか.

例えば次のようなスクリプトを用意し, `caller.sh`を実行すると, `echo-sleep5.sh`のプロセスは数秒で消えてしまいます.

(`/bin/sh`の実体は`dash`, `no_such_command`は存在しないもので, ここで失敗することを意図)

# caller.sh

[code]

    #! /bin/sh -e
    
    /path/to/echo-sleep5.sh | no_such_command &
    
[/code]

# echo-sleep5.sh

[code]

    #! /bin/bash
    
    # set -e
    
    i=0
    while [ true ]; do
      sleep 5
      echo "$i"
      i=$(($i+1))
    done
    
[/code]

しかし`caller.sh`の内容を`/etc/rc.local`に書く場合だと(シバンも同じ), `echo-sleep5.sh`内で`set
-e`しないと, プロセスが残り続けてしまいます.

どういう原理でこういう違いがでるのでしょうか？

# 追記

環境はRaspbian Jessieです. `systemd`の`rc-local.service`経由で起動しています.


CC BY-SA 3.0
2016-10-26T12:12:16.067
29919
2020-07-25T04:05:26.473
2016-10-27T03:17:21.103
13199
13199
question
shellscript
シェルの-eオプションとrc.localについて


手元（Ubuntu 16.04）で確認したところ、`echo-sleep5.sh`の`echo "$i"`を`echo "$i" >
/tmp/foo`とリダイレクトするように変更したら、コマンドラインから`caller.sh`しても`echo-sleep5.sh`のプロセスが残りました。

その先を追ってないので想像ですが、I/Oがエラーになるかどうかの違いではないでしょうか。


CC BY-SA 3.0
2016-10-29T05:41:52.870
29976
2016-10-29T05:41:52.870
4010
29919
answer


他の方のコメントを参考に実機を使って調査しました。  
プロセスが残るのは、`SIGPIPE`を無視しているか、いないかの違いでした。  
`set -e`は仕様どおりの振る舞いをしています。

> 例えば次のようなスクリプトを用意し, caller.shを実行すると, echo-sleep5.shのプロセスは数秒で消えてしまいます.

シグナル`SIGPIPE`のデフォルトの動作はプロセスの終了です。`SIGPIPE`がマスクされていないため、「書き込みエラー: Broken
pipe」が発生すると`SIGPIPE`を受信し、プロセス終了します。

> しかしcaller.shの内容を/etc/rc.localに書く場合だと(シバンも同じ), echo-sleep5.sh内でset -eしないと,
> プロセスが残り続けてしまいます.  
>  どういう原理でこういう違いがでるのでしょうか？

systemdのrc-local.service経由で起動すると、シグナル`SIGPIPE`を無視するようになります。このため「書き込みエラー:
Broken pipe」が発生してもプロセスは終了しません。  
straceで確認してみましたが、`set
-e`を実行した場合、エラーを検出したときにシェルスクリプトは自ら終了していました(`exit_group(1)`)。

また、実際にシグナルが無視する設定になっているかを確認してみました。  
スクリプトの中で`cat /proc/self/status`を実行してみたところ、SigIgnの行は以下のとおりでした。  
`SigIgn: 0000000000001006` (16進数です、1006は2進数で0001 0000 0000 0110)  
`SIGPIPE`のシグナル番号は13です。  
右から13ビット目はONなので`SIGPIPE`は無視されます。

> スクリプトを/etc/rc.localに書いた場合とそうでない場合で-eオプションの振舞いが異なるように見えるのですが, なぜでしょうか.

-eオプションの振舞いは異なりません。

/etc/rc.localに書いた場合、「書き込みエラー: Broken
pipe」が発生したとき、-eオプションをつけるとプロセスは終了し、-eオプションをつけないとプロセスは終了しません。

/etc/rc.localに書かずに直接実行した場合、-eオプションをつけても、つけなくても、プロセスは終了します。つけない場合でもシグナル受信で終了するからです。  
-eオプションをつけない場合、試しにシグナル`SIGPIPE`を無視(trap "" 13)してみると、プロセスは終了しませんでした。

動作を確認した環境です。  
Raspbian GNU/Linux 10 (buster)


CC BY-SA 4.0
2020-07-25T04:05:26.473
68930
2020-07-25T04:05:26.473
35558
29919
answer