"IFS=;"代わりに「IFS = Read」が頻繁に使用されるのはなぜですか?

"IFS=;"代わりに「IFS = Read」が頻繁に使用されるのはなぜですか?

一般的な慣行は、各繰り返しごとに繰り返し設定しないように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値に) IFSread(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$PATHIFS=; while read …IFS:

IFSもちろん、実行後にその値を復元できますread。ただし、これには以前の値を知る必要があります。これには追加の努力が必要です。IFS= read最も簡単な方法です(そして便利には最も短い方法です)。

1そしてトラップ信号によって中断されるとread、トラップの実行中になる可能性があります。これは、POSIXで指定されておらず、実際にはシェルによって異なります。

答え3

イディオムとイディオムの間の(すでに明確な)範囲指定の違い(コマンドごとのスクリプト/シェルの範囲変数の範囲指定)を除いて、最もIFS重要なレッスンは先行項目を失うことです。while IFS='' readIFS=''; while readwhile IFS=''; readIFSそして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

関連情報