IFS
以下は、このサイトとStackOverflowのしくみを理解するのに役立ついくつかのトピックです。
しかし、いくつかの簡単な質問があります。私はそれが将来の読者にとってより良い助けになると思ったので、同じ記事で彼らに尋ねることにしました。
Q1. IFS
一般に、「フィールド分割」という文脈で議論されている。はいフィールド分割そして噴射?
質問2:POSIX仕様説明する:
IFS 値が空の場合、フィールド分割は行われません。
IFS=
nullに設定するのと同じように設定していますかIFS
?これが意味する設定ですかempty string
?
Q3:POSIX仕様では読む次のように:
IFSが設定されていない場合、シェルはIFS値が次のように機能します。
<space>, <tab> and <newline>
デフォルト値を復元したいとしますIFS
。どうすればいいですか? (より具体的には、<tab>
どうやって参照しますか<newline>
?)
Q4:最後に、このコードの仕組みは次のとおりです。
while IFS= read -r line
do
echo $line
done < /path_to_text_file
最初の行を次のように変更すると、
while read -r line # Use the default IFS value
または:
while IFS=' ' read -r line
答え1
- はい、同じです。
- はい。
- bashと同様のシェルでも同様のことができます
IFS=$' \t\n'
。それ以外の場合は、テキスト制御コードの挿入を使用できます[space] CTRL+V [tab] CTRL+V [enter]
。ただし、これを行う予定の場合は、別の変数を使用して以前の値IFS
を一時的に保存してから後で復元することをお勧めします(または1つのコマンドで以前の値を一時的に上書きする構文を使用することvar=foo command
)。 -
$line
最初のコードスニペットは、単語の区切りを実行するフィールド区切り文字がないため、読み取った行全体をそのまま表示します。ただし、多くのシェルが文字列を格納するためにcstringを使用しているため、NULの最初のインスタンスがまだ早期に終了する可能性があることに注意してください。- 2番目のスニペットは、入力の正確なコピーを挿入できない場合があります
$line
。たとえば、連続するフィールド区切り文字が複数ある場合、最初の要素の単一インスタンスとして作成されます。これは通常、周囲のスペースの損失と見なされます。 - 3番目のスニペットは、スペース(通常のスペース、タブ、または改行ではない)に分割されていることを除いて、2番目と同じことを行います。
答え2
質問 1: はい。 「フィールド分割」と「単語分割」は、同じ概念を表す2つの用語である。
質問2:はい。設定しない場合IFS
(つまり、後)(空白、タブ、改行)に設定するのとunset IFS
同じです。 null値に設定されている場合(ここでは「null」が意味されます)(またはそれ以降)、フィールド分割はまったく行われません(そして一般的に使用される最初の文字は空白文字です)。IFS
$' \t\n'
IFS
IFS=
IFS=''
IFS=""
$*
$IFS
Q3:デフォルトの動作が必要な場合はIFS
使用できますunset IFS
。このデフォルトを明示的に設定するには、IFS
リテラル文字のスペース、タブ、改行を一重引用符で囲むことができます。 ksh93、bash、またはzshでは.portableを使用できますIFS=$' \t\n'
。ソースファイルのリテラルタブを避けるために使用できます。
IFS=" $(echo t | tr t \\t)
"
Q4:IFS
nullに設定する場合は、終了改行文字を除くフル行にread -r line
設定してください。line
を使用すると、先頭IFS=" "
と末尾のスペースが切り捨てられます。デフォルト値を使用すると、IFS
タブとスペースが切り捨てられます。
答え3
Q1.フィールド分割。
フィールド分割は単語分割と同じですか?
はい、どちらも同じ考えを指します。
Q2: いつIFSが空? 。
設定は
IFS=''
nullに等しく、空の文字列と同じですか?
はい、どちらも同じ意味です。フィールド/単語分割を実行しないでください。また、これはフィールドの印刷に影響し、(と同じecho "$*"
)すべてのフィールドはスペースなしで一緒にリンクされます。
Q3:(パートa)IFS設定を解除します。
POSIX仕様では以下を読んでください:
IFSが設定されていない場合、シェルはIFS値が次のように機能します。<スペース> <タブ> <改行>。
これは、以下とまったく同じです。
を使用すると、
unset IFS
シェルはIFSがデフォルトのように動作します。
これは、フィールド分割がデフォルトのIFS値とまったく同じまたは設定されていないことを意味します。
これは、IFSがすべての条件で同じように機能するという意味ではありません。より具体的に実行すると、OldIFS=$IFS
varOldIFS
は次のように設定されます。無効、デフォルトではありません。 IFSをリセットしてみてください。これにより、IFS=OldIFS
IFSを以前のように設定していないままにするのではなく、nullに設定されます。注意深い! ! 。
Q3:(パートb)IFSを復元します。
IFS 値をデフォルト値に復元する方法。 IFSをデフォルト値に復元したいとしましょう。どうすればいいですか? (具体的にはどのように参照できますか?<タブ>そして<改行>? )
zsh、ksh、bash(AFAIK)の場合、IFSをデフォルト値に設定できます。
IFS=$' \t\n' # works with zsh, ksh, bash.
すべて読んだら、他の内容を読む必要はありません。
ただし、shのIFSをリセットする必要がある場合は複雑になる可能性があります。
(複雑さを除く)欠点のない最も簡単な方法から見てみましょう。
1.- IFS設定を解除します。
私たちはできますunset IFS
(上記のQ3部分をお読みください)。
2.-キャラクターを交換してください。
回避策として、タブと改行値を変更すると、IFS値をより簡単に設定でき、これは同等の方法で機能します。
IFSを次に設定<スペース> <改行> <タブ>:
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.-簡単ですか?解決策:
IFSを正しく設定する必要がある添え字がある場合は、いつでも手動で作成できます。
IFS = ' '
手動で入力した順序は次のとおりです。IFS=
'spacetabnewline'上記の順序は実際に正しく入力されています(確認が必要な場合はこの回答を編集してください)。ただし、ブラウザでスペースを圧縮/非表示にするため、ブラウザからコピー/貼り付けが中断されます。このため、上記で書かれたコードを共有することは困難です。
4.-完璧なソリューション。
コピーに安全なコードを書くには、明示的に印刷可能なエスケープが必要なことがよくあります。
期待値を「生成」するコードが必要です。ただし、概念的には正確ですが、このコードは末尾を設定しません\n
。
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
これは、ほとんどのシェルで拡張すると、末尾の改行$(...)
や`...`
コマンドの置き換えが削除されるために発生します。
私たちはいたずらshの場合:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
別のアプローチは、IFSをbash(たとえば)の環境値に設定し、次のようにsh(環境を介してIFSを設定するバージョンを許可する)を呼び出すことです。
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
簡単に言えば、shはIFSをデフォルト値にリセットするのをやや奇妙な冒険にします。
Q4:実際のコードでは:
最後に、このコードの仕組みは次のとおりです。
while IFS= read -r line do echo $line done < /path_to_text_file
最初の行を次のように変更すると、
while read -r line # Use the default IFS value
または:
while IFS=' ' read -r line
echo $line
まず、(引用符でないvar)がporpouseに存在するかどうかわかりません。読み取りにはない2番目のレベルの「フィールド分割」が導入されました。だから、両方に答えます。 :)
このコードを使用してください(確認できるように)。必要です。便利なxxd:
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
私は得る:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
最初の値が正しい値です。IFS=
'spacetabnewline'
次の行は、$a
varが持つすべての16進値です。各読み取りコマンドに与えられるので、最後に改行 "0a"があります。
空のIFSを含む次の行は「フィールド分割」を実行しませんが、改行文字は期待どおりに削除されます。
次の 3 行の場合、IFS にスペースが含まれているため、初期スペースは削除され、var 行は残りの残高に設定されます。
最後の4行は、引用符で囲まれていない変数が何をするかを示しています。値は(複数の)スペースに分割され、次のように印刷されます。bar,baz,qux,
答え4
unset IFS
IFSが後で「\ t \ n」と仮定されても、IFSを消去します。
$ echo "'$IFS'"
'
'
$ IFS=""
$ echo "'$IFS'"
''
$ unset IFS
$ echo "'$IFS'"
''
$ IFS=$' \t\n'
$ echo "'$IFS'"
'
'
$
Bash バージョン 4.2.45 および 3.2.25 でテストされており、同じ動作があります。