web-dev-qa-db-ja.com

シェルスクリプトでファイル記述子を群がらせる

これで途切れることはないと思いましたbegin-endペアですが、そうではありません:

#!/bin/bash
fun()(
    flock 1 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait

私は何が間違っているのですか?

5
PSkocik

失敗の理由は man 2 flock

Flock()によって作成されたロックは、開いているファイルの説明に関連付けられています(open(2)を参照)。これは、重複するファイル記述子(たとえば、fork(2)またはdup(2)によって作成される)が同じロックを参照し、このロックはこれらの記述子のいずれかを使用して変更または解放できることを意味します。

これは、すべてのプロセスが同じファイル記述子を継承するため、そのうちの1つがロックを実行すると、すべてのプロセスがそれを共有することを意味します。また、同じファイル記述子を2回ロックすることはできません。

このようなものに対する私の通常の解決策は、スクリプト自体をロックすることです(ただし、スクリプトを同時に複数回実行すると問題が発生します)。

#!/bin/bash
fun()(
    exec 3<"$0"
    flock 3 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
    echo "$BASHPID begin"
    sleep 1;
    echo "$BASHPID end"
)
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
fun &
wait
3
Patrick

このアプローチは機能します:

fun()(
  (flock 9 || { echo >&2 "$BASHPID: FAIL: $?"; exit 1; }
   echo "$BASHPID begin"
   sleep 1;
   echo "$BASHPID end"
  ) 9>test
)

これにより、保護する必要のあるコマンドが終了していない限り、ロックが保持されているファイルが閉じられないことが保証されます。 (明らかに、testをより適切なものに置き換える必要がありますmktempを使用します。)

4
Stephen Kitt