58

次の Bash スクリプトがあるとします。

while read SCRIPT_SOURCE_LINE; do
  echo "$SCRIPT_SOURCE_LINE"
done

最後に改行がないファイルの場合、これにより最後の行が効果的にスキップされることに気付きました。

私は解決策を探し回り、これを見つけました

読み取りが行末ではなくファイルの終わりに達すると、データを読み取って変数に割り当てますが、ゼロ以外のステータスで終了します。ループが構築されている場合は、「読み取り中;実行中;実行中」

そのため、読み取り終了ステータスを直接テストする代わりに、フラグをテストし、読み取りコマンドでループ本体内からそのフラグを設定します。そうすれば、読み取りの終了ステータスに関係なく、ループ本体全体が実行されます。これは、読み取りが他のコマンドと同様にループ内のコマンドのリストの 1 つにすぎず、ループが実行されるかどうかの決定要因ではないためです。

DONE=false
until $DONE ;do
read || DONE=true
# process $REPLY here
done < /path/to/file.in

while入力ファイルの場所をハードコーディングせずに、以前のループとまったく同じように動作するように、このソリューションを書き直すにはどうすればよいですか?

4

8 に答える 8

42

私は次の構成を使用します:

while IFS= read -r LINE || [[ -n "$LINE" ]]; do
    echo "$LINE"
done

入力内のヌル文字を除いて、ほとんどすべてで機能します。

  • 空白行で開始または終了するファイル
  • 空白で開始または終了する行
  • 終了改行がないファイル
于 2011-02-15T23:17:49.663 に答える
17

最初の例では、 stdinから読み取っていると想定しています。2 番目のコード ブロックで同じことを行うには、リダイレクトを削除して $REPLY をエコーするだけです。

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY
done
于 2010-11-12T13:41:40.093 に答える
5

grepwhile ループでの使用:

while IFS= read -r line; do
  echo "$line"
done < <(grep "" file)

grep .の代わりに使用するgrep ""と、空の行がスキップされます。

ノート:

  1. を使用IFS=すると、行のインデントがそのまま保持されます。

  2. ほとんどの場合、読み取りでは -r オプションを使用する必要があります。

  3. 末尾に改行がないファイルは、標準の UNIX テキスト ファイルではありません。

于 2015-07-14T04:49:06.153 に答える
4

readの代わりに、 teecatなどのGNU Coreutilsを使用してみてください。

標準入力から

readvalue=$(tee)
echo $readvalue

ファイルから

readvalue=$(cat filename)
echo $readvalue
于 2016-11-03T14:23:49.197 に答える
1

ここでの基本的な問題は、readEOF に遭遇したときにエラーレベル 1 を返すことです。たとえ変数を正しくフィードしてもです。

readしたがって、ループですぐにerrorlevel を使用できます。そうしないと、最後のデータが解析されません。しかし、これを行うことができます:

eof=
while [ -z "$eof" ]; do
    read SCRIPT_SOURCE_LINE || eof=true   ## detect eof, but have a last round
    echo "$SCRIPT_SOURCE_LINE"
done

行を解析するための非常に確実な方法が必要な場合は、次を使用する必要があります。

IFS='' read -r LINE

次のことを覚えておいてください。

  • NUL 文字は無視されます
  • echoの動作を模倣するために使用することに固執する場合は、EOF が検出されたときcatに強制する必要があります(条件を使用できます) 。echo -n[ "$eof" == true ]
于 2015-01-03T09:18:48.383 に答える
0

@Netcoderの答えは良いです。この最適化により、偽の空白行が排除され、元の行がそうであった場合、最後の行に改行を含めることもできます。

DONE=false
NL=
until $DONE ;do
if ! read ; then DONE=true ; NL='-n ';fi
echo $NL$REPLY
done

これの変形を使用して 2 つの関数を作成し、'[' を含むテキストのパイピングを許可して grep を満足させました。(他の翻訳を追加できます)

function grepfix(){
    local x="$@";
    if [[ "$x" == '-' ]]; then
      local DONE=false
      local xx=
      until $DONE ;do
         if ! IFS= read ; then DONE=true ; xx="-n "; fi
         echo ${xx}${REPLY//\[/\\\[}
      done
    else
      echo "${x//\[/\\\[}"
    fi
 }


 function grepunfix(){
    local x="$@";
    if [[ "$x" == '-' ]]; then
      local DONE=false
      local xx=
      until $DONE ;do
         if ! IFS= read ; then DONE=true ; xx="-n "; fi
         echo ${xx}${REPLY//\\\[/\[}
      done
    else
      echo "${x//\\\[/\[}"
    fi
 }

(渡す - $1 はパイプを有効にするため、それ以外の場合は引数を変換するだけです)

于 2016-06-07T09:44:34.653 に答える