web-dev-qa-db-ja.com

ループ出力の配管により、ローカル変数の変更が防止されます

引数として多数のファイルやディレクトリを使用する単純なbash関数を作成しようとしています。そうすべき:

  1. ファイル名を完全修飾します。
  2. それらを並べ替えます。
  3. 重複を削除します。
  4. 実際に存在するものをすべて印刷します。
  5. 存在しないファイルの数を返します。

ほぼやりたいことを実行するスクリプトがありますが、並べ替えに失敗します。現状のスクリプトの戻り値は正しいですが、出力は正しくありません(ソートされておらず、重複しています)。示されているように| sort -uステートメントのコメントを外すと、出力は正しいですが、戻り値は常に0です。

N.B.問題を解決するためのより簡単な解決策は歓迎されますが、問題は本当にこれが私が持っているコードで発生する理由についてです。つまり、パイプを追加すると、スクリプトが変数rをインクリメントするのを停止するように見えるのはなぜですか?

スクリプトは次のとおりです。

function uniqfile
{
    local r=0 

    for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done #| sort -u    ## remove that comment

    return $r
}
11
tjm

これはよく知られているbashの落とし穴です。これが原因ですfeature

パイプラインの各コマンドは、個別のプロセスとして(つまり、サブシェルで)実行されます。

変更された変数がサブシェルに対してローカルであり、親に戻ると表示されないようにします。

これを回避するには、プロセス置換を使用して、パイプラインを回避するようにコードを言い換えます。

 for arg in "$@"
    do  
        readlink -e "$arg" || (( ++r ))

    done > >(sort -u)
15
enzotib