28031


exec で、既存の fd をコピーすることがあります。具体的には、次のように

[code]

    #!/bin/bash
    
    echo hoge | {
        exec 3<&0
        echo fuga | {
            from_stdin=$(cat)
            from_3=$(cat <&3)
            echo "From stdin: $from_stdin"
            echo "From 3: $from_3"
        }
        exec 3<&-
    }
    
[/code]

２つの入力から読み込むようなシェルスクリプトを記述するときに、利用したりしています。

このように、 exec で fd をコピーした際には、基本的に最後にクローズするようにしています。上の例で言うと `exec 3<&-` がそれです。

これは、なんとなく必要そうだから実行しているのですが、これが本当に必要だったのか、いまいち自信がないです。

質問:

  * exec でコピーした fd は、これは必ず上記のようにクローズする必要がありますか？ 
    * 必要がある場合、クローズしないと何が起きるのでしょうか。
    * 必要がない場合、例えば上のケースではどうしてその必要がないのでしょうか。


CC BY-SA 3.0
2016-08-02T10:07:16.977
28020
2016-08-03T01:35:40.640
2016-08-02T13:40:15.880
76
754
question
bash
exec でコピーした fd をクローズする必要性


`exec`は本来リダイレクトを行った上で子プロセスを立ち上げるものです（立ち上げるというか切り替えるというか…）。その子プロセスは`{}`グループまで実行されそこで終了します。ですのでプロセス終了直前に`exec
3<&-`でfd 3だけを閉じる意義があるかどうかという話になると思いますが、私は閉じる意義はないと考えます。

なお、従来の`/bin/sh`であれば`{}`グループに対して本当に子プロセスを立ち上げていたと思いますが、Bashはパフォーマンスのために子プロセスを立ち上げたかのように振る舞いますので、fd等も適切に管理されているはずです。


CC BY-SA 3.0
2016-08-02T13:51:02.903
28026
2016-08-02T13:51:02.903
4236
28020
answer


fd
は、それを開けたプロセスに束縛され、そのプロセスが死んだ時点で勝手にクローズされます。なので、短いシェルスクリプトであれば、わざわざクローズする必要はありません。

大規模なスクリプトを作成したり、誰かと共同で作業する場合で、かつサブプロセス内に新規作成する fd を閉じ込めておけない場合、「使用してない fd
番号を見つけ出し、その fd 番号に fd 0
をコピーする」といったような面倒な作業が必要になるかもしれません。そういうのはやたら面倒だしスクリプトが読みづらくなるので、大抵は「fd3
は一時的に使ってよし、ただし他でも使うかもしれないから要らなくなったらすぐ閉じること」とすることになります（いちいちそんなの明文化せず、fd3
って普通は空いてるよね〜ぐらいの感覚だと思います）。

ただし、スクリプトの生存期間にかかわらず要らなくなった fd
はクローズしておいた方が分かりやすい（クローズされてないということは、また使われるかもしれないとコードの読み手は考えます）し、将来スクリプトが長くなった時にバグのもとにならずに済むので、完全に個人用のスクリプトでない限りは、要らなくなった
fd はクローズするものと考えておけばいいでしょう。

この例では、`exec` がパイプライン `|`
の一部で使われてる、すなわちサブシェル内なので、明示的なクローズの必要はありません。サブシェルが終わると同時に勝手にクローズされます。


CC BY-SA 3.0
2016-08-02T23:41:37.163
28031
2016-08-03T01:35:40.640
2016-08-03T01:35:40.640
2521
2521
28020
answer