このスクリプトで待ってからすべてのサブシェルが実行されないのはなぜですか?

このスクリプトで待ってからすべてのサブシェルが実行されないのはなぜですか?

このスクリプトはすべてのgitリポジトリを取得します。

#!/bin/bash

find / -type d -name .git 2>/dev/null | 
while read gitFolder; do
    if [[ $gitFolder == *"/Temp/"* ]]; then
        continue;
    fi
    if [[ $gitFolder == *"/Trash/"* ]]; then
        continue;
    fi
    if [[ $gitFolder == *"/opt/"* ]]; then
        continue;
    fi
    parent=$(dirname $gitFolder);
    echo "";
    echo $parent;
    (git -C $parent pull && echo "Got $parent") &
done 
wait
echo "Got all"

waitすべてのサブシェルを待たないでくださいgit pull

なぜこれが起こるのですか?どうすれば解決できますか?

ベストアンサー1

問題はwait無効なシェルプロセスによって実行されています。では、bashパイプラインの各部分が別々のサブシェルで実行されます。バックグラウンドジョブはwhileループを実行するサブシェルに属します。このサブシェルに移動すると、期待waitどおりに機能します。

find ... |
{
    while ...; do
        ...
        ( git -C ... && ... ) &
    done
    wait
}

echo 'done.'

あなたも少し持っています。引用符なし変数

パイプを完全に取り外し、代わりにループを直接実行してくださいfindこれにより、出力を解析する必要がなくなります。find

find / -type d -name .git \
    ! -path '*/Temp/*' \
    ! -path '*/opt/*' \
    ! -path '*/Trash/*' \
    -exec sh -c '
    for gitpath do
        git -C "$gitpath"/.. pull &
    done
    wait' sh {} +

または、-prune処理したくないサブディレクトリに移動したくない場合は、次のようにします。

find / \( -name Temp -o -name Trash -o -name opt \) -prune -o \
    -type d -name .git -exec sh -c '
    for gitpath do
        git -C "$gitpath"/.. pull &
    done
    wait' sh {} +

説明で述べたように、xargs同時に実行されるプロセスの数をよりよく制御することもできますgit。以下で使用されるオプション-P(同時ジョブ数の指定)は、非標準、-0(読み取り\0- 区切りパス名)、および-r(入力なしでコマンドを実行しない)です。ただし、GNUxargsとこのユーティリティの他のいくつかの実装にはこれらのオプションがあります。さらに、述語(出力で区切られたパス名)は非-print0標準ですが、通常は実装されています。find\0

find / \( -name Temp -o -name Trash -o -name opt \) -prune -o \
    -type d -name .git -print0 |
xargs -t -0r -P 4 -I {} git -C {}/.. pull

私はGNUが同様の方法で使用できると確信していますparallelが、それがこの質問の主な焦点ではないので、私はそのような考え方を追求しません。

おすすめ記事