web-dev-qa-db-ja.com

変数がbashwhileループでインクリメントされないように見えるのはなぜですか?

私はbashスクリプトにかなり慣れていません。 bashスクリプトのwhileループの最後に表示するカウント変数の正しい値を取得できないようです。

背景:私にはかなり単純なタスクがあります。ファイルパスのリストを含むテキストファイルをbashスクリプトに渡し、それらのファイルの存在をチェックして、既存/欠落ファイルの数をカウントしたいと思います。カウント部分を除いて、ほとんどのスクリプトを機能させました。

N=0
correct=0
incorrect=0
cat $1 | while read filename ; do
    N=$((N+1))
    echo "$N"

    if ! [ -f $filename ]; then

        incorrect=$((incorrect+1))
    else
        correct=$((correct+1))

    fi

done

echo "# of Correct Paths: $correct"
echo "# of Incorrect Paths: $incorrect"
echo "Total # of Files: $N"

5つのファイルのリストがあり、そのうち4つが存在する場合、次の出力が得られるはずです(echoループ内のwhileコマンドに注意してください)。

1
2
3
4
5
# of Correct Paths: 4
# of Incorrect Paths: 1
Total # of Files: 5

代わりに、次のようになります。

1
2
3
4
5
# of Correct Paths: 0
# of Incorrect Paths: 0 
Total # of Files: 0

これらの変数の値はどうなりましたか?グーグルは疑わしい品質の提案をたくさん持っていて、もう少し検索することでそれを機能させることができると思いますが、私が間違っていることの簡単な説明は非常に役に立ちます。

15
wwwilliam

これは、パイプで役に立たないcatコマンドを使用しているため、サブシェルが作成されているためです。 catなしで試してみてください:

while read filename ; do
    N=$((N+1))
    ....
done < file
23
kurumi

または、何らかの理由でcatを保持したい場合は、cat命令の前に次の行を追加するだけでスクリプトを修正できます。

shopt -s lastpipe 
1
jlliagre

より一般的には、コマンドの出力をパイプ処理したい場合があります。 プロセス置換 を使用してGitによってコミットされようとしているJavaScriptファイルをリントし、失敗したファイルの数をカウントする例を次に示します。

# $@ glob
git-staged-files() {
  git diff --cached -C -C -z --name-only --relative --diff-filter=ACMRTUXB "$@"
}

# $@ name
map() { IFS= read -rd $'\0' "$@"; }

declare -i errs=0
while map file; do
  echo "Checking $file..."
  git show ":$file"|
  eslint --stdin --stdin-filename "$file" || ((++errs))
done < <(git-staged-files \*.js)

((errs)) && echo -en "\e[31m$errs files with errors.\e[00m " >&2 || :
0