
一般的な慣行は、各繰り返しごとに繰り返し設定しないようにIFS設定をwhileループの外に置くようです...これは単なる慣例的な「猿が見て、猿がする」スタイルですか?読む男性は読むそれとも、ここで微妙な(または露骨に明らかな)問題を見逃しているのでしょうか?
答え1
罠
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'
。
この質問の2番目の部分ここにマージだから、ここから関連する回答を削除しました。
答え2
慎重に作成された入力テキストの例を見てみましょう。
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
バックスラッシュを食べる:backslash-newlineは改行を無視し、backslash-anythingは最初のバックスラッシュを無視します。バックスラッシュが特別に処理されるのを防ぐために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
変えるnull 値です。
$ 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
(null値に) IFS
read
(null値に)設定します.一般的なケースの例です簡単なコマンド構文:変数の割り当ての順序(空の場合があります)の後にコマンド名とその引数が続きます(いつでもリダイレクトを導入できます)。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
最も簡単な方法です(そして便利には最も短い方法です)。
1そしてトラップ信号によって中断されるとread
、トラップの実行中になる可能性があります。これは、POSIXで指定されておらず、実際にはシェルによって異なります。
答え3
イディオムとイディオムの間の(すでに明確な)範囲指定の違い(コマンドごとのスクリプト/シェルの範囲変数の範囲指定)を除いて、最もIFS
重要なレッスンは先行項目を失うことです。while IFS='' read
IFS=''; while read
while IFS=''; read
IFS
そして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"
)
答え4
からインスピレーションを受けるYuzemの答え
実際のキャラクターに設定したいならIFS
これが私にとって効果的でした。
iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
echo "$line"
done