ループ出力のパイピングは、ローカル変数の変更を防ぎます


11

私は、引数として多数のファイルやディレクトリを受け取る単純なbash関数を記述しようとしています。そうすべき:

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

私はほとんど私がやりたいことをするスクリプトを持っていますが、ソートに落ちます。現状のスクリプトの戻り値は正しいですが、出力は正しくありません(ソートされておらず、重複しています)。| sort -u示されているようにステートメントのコメントを外すと、出力は正しくなりますが、戻り値は常にになり0ます。

NB問題を解決するためのよりシンプルなソリューションは歓迎されますが、問題は、なぜこれが私が持っているコードで発生するのかについてです。つまり、パイプを追加すると、スクリプトが変数をインクリメントするように見えるのはなぜrですか?

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

function uniqfile
{
    local r=0 

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

    done #| sort -u    ## remove that comment

    return $r
}

ほんの小さな観察。あなたは減らすことができますfor arg in "$@"for arg。「もし「言葉で...」なら が存在しない場合、「in "$ @"」が想定されます。」-ヘルプ
manatwork '30 / 09/30

回答:


15

これは、この機能により、よく知られているbashの落とし穴です

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

そのため、変更された変数はサブシェルに対してローカルであり、親に戻されても表示されません。

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

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

    done > >(sort -u)

ありがとうございました。それは素晴らしいことです。>(..command..)構成の名前を教えていただけませんか。私それがどのように機能するかを知っていると思いますが、私はさらにいくつかの読書をするべきだと感じています。
TJM

2
@tjm:プロセス置換
enzotib

Bashのプロセス置換には多くの形式があります:tldp.org/LDP/abs/html/process-sub.html
slm

プロセス置換 は、コマンドの入力または出力をファイルとして表示できるプロセス間通信の形式です。コマンドは、コマンドシェルによって、ファイル名が通常発生するインラインで置換されます。これにより、通常はファイルのみを受け入れるプログラムが、別のプログラムから直接読み取りまたは書き込みを行うことができます。
nobar

3

| sort -u力は、先行するビットbashが「STDOUTを」内にリダイレクトする必要があります(サブプロセスで実行する(forループ全体そう)sort「STDIN」。(インターネットは考えているようだkshと、bash最初または最後の...若干異なる。このケースを処理パイプシーケンスのコマンドはサブシェルに入れられますか?)

このスレッドは同様の問題を解決し、最後にきちんとした解決策があります:http : //ubuntuforums.org/showthread.php?t=312017

抜粋
    #!/bin/bash
    exec 3< <(du | sort -n)  

    n=0
    while read size dir; do
      [ $size -gt 1000 ] && ((n++))
    done <&3
    exec 3<&-

    echo "Found $n too big files"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.