5

私が走ったら

$#/bin/bash
for i in `seq 5`; do
    exec 3> >(sed -e "s/^/$i: /"; echo "$i-")
    echo foo >&3
    echo bar >&3
    exec 3>&-
done

その場合、結果は同期しません。次のようになります。

1: foo
1: bar
2: foo
2: bar
1-
3: foo
3: bar
2-
3-
4: foo
5: foo
4: bar
5: bar
4-
5-

>(...)次のイテレーションに進む前に、プロセス置換が完了していることを確認するにはどうすればよいですか?

sleep 0.1後で挿入exec 3>&-することは役に立ちましたが、それはエレガントではなく、非効率的であり、常に機能することが保証されているわけではありません。

編集:例はばかげているように見えるかもしれませんが、それは説明のためだけのものでした。私がやっていることは、ループ内の入力のストリームを読み取り、ループ中に時々変化するプロセスに各行をフィードすることです。コードで簡単に説明:

# again, simplified for illustration
while IFS= read line; do
    case $line in
    @*)
        exec 3>&-
        filename=${line:1}
        echo "starting $filename"
        exec 3> >(sort >"$filename"; echo "finished $filename")
        ;;
    *)
        echo "$line" >&3
        ;;
    esac
done
exec 3>&-
4

5 に答える 5

5

coprocessesを使用して、bash 4 で次のように動作します。

#!/bin/bash
fd_re='^[0-9]+$'
cleanup_and_wait() {
    if [[ ${COPROC[1]} =~ $fd_re ]] ; then
        eval "exec ${COPROC[1]}<&-"
        echo "waiting for $filename to finish" >&2
        wait $COPROC_PID
    fi
}

while IFS= read -r line; do
    case $line in
    @*)
        cleanup_and_wait
        filename=${line:1}
        echo "starting $filename" >&2
        coproc { sort >"$filename"; echo "Finished with $filename" >&2; }
        ;;
    *)
        printf '%s\n' "$line" >&${COPROC[1]}
        ;;
    esac
done
cleanup_and_wait

以前のバージョンの bash では、代わりに名前付きパイプを使用できます。

cleanup_and_wait() {
    if [[ $child_pid ]] ; then
      exec 4<&-
      echo "waiting for $filename to finish" >&2
      wait $child_pid
    fi
}

# this is a bit racy; without a force option to mkfifo,
# however, the race is unavoidable
fifo_name=$(mktemp -u -t fifo.XXXXXX)
if ! mkfifo "$fifo_name" ; then
  echo "Someone else may have created our temporary FIFO before we did!" >&2
  echo "This can indicate an attempt to exploit a race condition as a" >&2
  echo "security vulnarability and should always be tested for." >&2
  exit 1
fi

# ensure that we clean up even on unexpected exits
trap 'rm -f "$fifo_name"' EXIT

while IFS= read -r line; do
    case $line in
    @*)
        cleanup_and_wait
        filename=${line:1}
        echo "starting $filename" >&2
        { sort >"$filename"; echo "finished with $filename" >&2; } <"$fifo_name" &
        child_pid=$!
        exec 4>"$fifo_name"
        ;;
    *)
        printf '%s\n' "$line" >&4
        ;;
    esac
done
cleanup_and_wait

いくつかのメモ:

  • printf '%s\n' "$line"よりも安全に使用できecho "$line"ます。たとえば、行に のみが含まれ-eている場合、一部のバージョンのechoはそれに対して何もしません。
  • クリーンアップに EXIT トラップを使用すると、予期しない SIGTERM またはその他のエラーによって、古い fifo がそのまま放置されることがなくなります。
  • プラットフォームが単一のアトミック操作で不明な名前の FIFO を作成する方法を提供している場合は、それを使用してください。これにより、mkfifo が成功したかどうかを常にテストする必要がある状況を回避できます。
于 2012-06-21T03:09:12.440 に答える
4

簡単です。すべてを cat にパイプするだけです。

#!/bin/bash
for i in `seq 5`; do
  {
  exec 3> >(sed -e "s/^/$i: /"; echo "$i-")
  echo foo >&3
  echo bar >&3
  exec 3<&-
  }|cat
done

出力は次のとおりです。

1: foo
1: bar
1-
2: foo
2: bar
2-
3: foo
3: bar
3-
4: foo
4: bar
4-
5: foo
5: bar
5-
于 2015-06-12T04:40:08.820 に答える
3
mkfifo tmpfifo
for i in `seq 5`; do
  { sed -e "s/^/$i: /"; echo "$i-";} <tmpfifo &
  PID=$!
  exec 3> tmpfifo
  echo foo >&3
  echo bar >&3
  exec 3>&-
  wait $PID
done
rm tmpfifo
于 2012-06-21T09:37:57.483 に答える
1

「明白な」答えは、プロセス置換を取り除くことです。

for i in `seq 5`; do
    echo foo | sed -e "s/^/$i: /"; echo "$i-"
    echo bar | sed -e "s/^/$i: /"; echo "$i-"
done

問題は、プロセス置換を使用してコードを構造化する必要があるかどうかです。上記は、非同期構造を同期しようとするよりもはるかに簡単です。

于 2012-06-21T01:32:53.210 に答える