回答:
わなは
IFS=; while read..
IFS
ループ外のシェル環境全体に設定しますが、
while IFS= read
read
呼び出しに対してのみ再定義します(Bourneシェルを除く)。次のようなループを行うことを確認できます
while IFS= read xxx; ... done
そのようなループの後、echo "blabalbla $IFS ooooooo"
印刷します
blabalbla
ooooooo
一方、後に
IFS=; read xxx; ... done
再定義されたIFS
滞在:現在echo "blabalbla $IFS ooooooo"
印刷
blabalbla ooooooo
したがって、2番目のフォームを使用する場合は、リセットすることを忘れないでくださいIFS=$' \t\n'
。
while
ための条件-あまり意味がありませんwhile
両端そのセミコロンで、その実際のループはありません... read
1要素のループ内だけで最初のコマンドになり...か?何についてdo
、その後..?
while
条件にいくつかのコマンドを持つことができます(前にdo
)。
IFS=
が、IFS=X
機能しません...(または、しばらくの間、これについてODしました..コーヒーブレークが必要です:)
入念に作成された入力テキストを使用した例を見てみましょう。
text=' hello world\
foo\bar'
これは2行で、最初はスペースで始まり、バックスラッシュで終わります。最初に、予防策を講じずに何が起こるかを見てみましょうread
(ただし、拡張のリスクなしprintf '%s\n' "$text"
に慎重に印刷するために使用します$text
)。(以下$
は、シェルプロンプトです。)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
バックスラッシュを使い切ってください:バックスラッシュ-改行は改行を無視し、バックスラッシュ-何でもその最初のバックスラッシュを無視します。バックスラッシュが特別に扱われるのを避けるために、を使用しますread -r
。
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
それは良いことです、予想通り2行あります。2行は、ほぼ所望のコンテンツが含まれていますとの間の二重のスペースhello
とがworld
保持されている、それは内だからline
変数。一方、最初のスペースは使い果たされました。これread
は、最後の変数に残りの行が含まれていることを除いて、変数に渡すだけの数の単語を読み取るためです。ただし、最初の単語で始まります。
したがって、各行を文字通り読み取るためには、単語の分割が行われていないことを確認する必要があります。これを行うには、IFS
変数を空の値に設定します。
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
組み込みIFS
の期間に特にread
設定する方法に注意してください。IFS= read -r line
設定環境変数IFS
、具体的に実行するための(空値)read
。これは、一般的な単純なコマンド構文のインスタンスです。コマンド名とその引数が後に続く(おそらく空の)変数割り当てのシーケンスです(また、いつでもリダイレクトをスローできます)。以来read
ビルトインされ、変数は実際には外部プロセスの環境で終わることはありません。それにもかかわらず、の値は、実行中で$IFS
あればそこに割り当てているものです¹ read
。read
は特別なビルトインではないため、割り当てはその期間だけ持続します。
したがってIFS
、それに依存する可能性のある他の命令の値を変更しないように注意しています。このコードは、周囲のコードがIFS
最初に設定したものに関係なく機能し、ループ内のコードがに依存していても問題は発生しませんIFS
。
コロンで区切られたパスでファイルを検索するこのコードスニペットとは対照的です。ファイル名のリストは、ファイルから1行に1ファイル名ずつ読み取られます。
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
ループであった場合while IFS=; read -r name; do …
は、for dir in $PATH
分割しないでしょう$PATH
コロンで分離された成分に。コードがの場合、ループ本体で設定されていないIFS=; while read …
ことIFS
はさらに明白です:
。
もちろん、IFS
実行後にの値を復元することは可能read
です。しかし、それは以前の値を知る必要があり、それは余分な努力です。IFS= read
簡単な方法です(便利なことに、最短の方法でもあります)。
¹ そして、read
おそらくトラップの実行中に、トラップされた信号によって中断された場合-これはPOSIXによって指定されておらず、実際にはシェルに依存します。
while IFS= read
(後にセミコロンなしで=
)ではない特殊な形式のwhile
かのIFS
かのread
例:構築物は、一般的な..です。anyvar=anyvalue anycommand
。欠如;
に設定した後は、anyvar
の範囲になりanyvar
地元にanycommand
しばらく..を- /行わないループは100%であり、無関係のローカルスコープにany_var
。
、およびイディオム(コマンドごととスクリプト/シェル全体の変数スコープ)の(既に明確になっている)IFS
スコープの違いは別として、持ち帰りのレッスンは、IFS変数の場合、入力行の先頭と末尾のスペースを失うことです(aを含む)スペースに設定されます。while IFS='' read
IFS=''; while read
while IFS=''; read
IFS
ファイルパスが処理されている場合、これはかなり深刻な結果を招く可能性があります。
したがって、IFS変数を空の文字列に設定することは、行の先頭と末尾の空白が削除されないことを保証するため、お勧めできません。
関連項目:Bash、IFSを使用してファイルから1行ずつ読み取る
(
shopt -s nullglob
touch ' file with spaces '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)
while IFS=X read
で分割しませんX
が、while IFS=X; read
ありません...