テキストファイル(またはパイプで接続された出力、ここでは重要ではありません)があります。
memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
memcached.cmd_set 17641364
memcached.cmd_flush 0
このコマンドを使用すると、cat test.txt | while read i; do echo $i; done
かなり予想される出力が生成されます。
memcached.uptime 1061374
memcached.curr_connections 480
memcached.cmd_get 478962548
etc
ただし、繰り返すとfor i in $(cat test.txt); do echo $i; done
他の内容が表示されます。
memcached.uptime
1061374
memcached.curr_connections
480
memcached.cmd_get
478962548
etc
問題は:なぜ? ? ?
答え1
存在する:
cat test.txt | while read i; do echo $i; done
あなたはかなり多くのシェルスクリプティング悪い習慣を落とすことにしました。
まず言及すべきですがシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?。
たとえば、次のように入力します。
-n
/*/*/*/../../../*/*/*
foo\
bar
シェルループを使用する必要がある場合は、おそらく次のようになります。
{
while IFS= read <&3 -r i; do
printf '%s\n' "$i" || exit
done
[ -z "$i" ] || printf %s "$i" || exit
} 3< test.txt
存在する
for i in $(cat test.txt); do echo $i; done
これは別の悪い習慣に置き換えることができます。ここに引用しなかった妥当な理由があります$(cat test.txt)
。分割+グローブ演算子の分割部分が必要ですが、分割する項目を指定してグローブ部分を無効にすることを忘れました。
IFS='
' # split on newline only. The default value of $IFS
# contains space, tab and newline which explains why you see
# one word per line
set -o noglob # disable glob
for i in $(cat test.txt); do
printf '%s\n' "$i" || exit
done
ループを開始する前に、空白行はまだスキップされ、ファイルの内容を読み取り、メモリに保存します(複数回)。
答え2
答えは、$(command)
コマンドの生出力に拡張することです。これにより、シェルはこれに対して一般的な単語分割を実行します。分離には以下が含まれます。どのスペースは単語区切り文字として扱われます。
また、テキストの解析の1つで2つの異なる操作を実行していますread
。ワイヤーなしで入力言葉$(cat)
もう一つはループで出力を繰り返すことですfor
。同様の結果を得ることもできますIFS='\n' for i in $(cat test.txt); do echo "$i"; done
。