IFSを理解する


71

このサイトとStackOverflowの以下のいくつかのスレッドは、IFS動作の仕組みを理解するのに役立ちました。

しかし、まだ短い質問がいくつかあります。私はそれがより良い将来の読者を助けるかもしれないと思うので、同じ投稿で彼らに尋ねることに決めました:

Q1。 IFS通常、「フィールド分割」のコンテキストで説明されています。あるフィールド分割は、同じ単語の分割

Q2: POSIX仕様に次のように書かれています

IFSの値がヌルの場合、フィールド分割は実行されません。

nullに設定IFS=するのと同じ設定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

回答:


28
  1. はい、彼らは同じです。
  2. はい。
  3. bashおよび同様のシェルでは、のようなことができますIFS=$' \t\n'。それ以外の場合は、を使用してリテラル制御コードを挿入できます[space] CTRL+V [tab] CTRL+V [enter]。ただし、これを行う予定がある場合は、別の変数を使用して古いIFS値を一時的に保存し、後で復元する(またはvar=foo command構文を使用して1つのコマンドで一時的に上書きする)方が適切です。
    • 最初のコードスニペットは、$line単語分割を実行するフィールドセパレータがないため、読み取った行全体を逐語的に配置します。ただし、多くのシェルはcstringを使用して文字列を格納するため、NULの最初のインスタンスによって、NULの外観が途中で終了する可能性があります。
    • 2番目のコードスニペットは、入力の正確なコピーをに入れない場合があります$line。たとえば、複数の連続したフィールドセパレータがある場合、それらは最初の要素の単一のインスタンスになります。これは、多くの場合、周囲の空白の損失として認識されます。
    • 3番目のコードスニペットは2番目のコードスニペットと同じように動作しますが、スペースでのみ分割されます(通常のスペース、タブ、または改行ではありません)。

3
Q2への答えは間違っています。空IFSと未設定IFSは非常に異なります。Q4の答えは部分的に間違っています。ここでは、内側の区切り文字には触れず、先頭と末尾の区切り文字のみに触れます。
ジル

3
@Gillesは三つ所与の金種のQ2のいずれも未設定を意味しないでIFS、それらのすべてが意味しますIFS=
ステファンギメネス

@Gilles第2四半期に、私は彼らが同じだとは言いませんでした。そして、以下に示すように、内側のセパレーターに触れますIFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"。(えー、なに?そこに複数のスペース区切り文字があるはずです。SOエンジンはそれらを削除し続けます)。
クリスダウン

2
@StéphaneGimenez、Chris:ああ、そうです、Q2についてすみません、質問を読み違えました。第4四半期についてはread、最後の変数は、最後の区切り文字を除く残りのすべてを取得し、内部の区切り文字を内部に残します。
ジル

1
Gillesは、読み取りによって削除されないスペースについて部分的に正しいです。詳細については私の答えを読んでください。

22

Q1:はい。「フィールド分割」と「単語分割」は同じ概念の2つの用語です。

Q2:はい。場合IFS(つまり、後に設定されていないunset IFS)、同等であるIFSに設定されている$' \t\n'(スペース、タブおよび改行)。場合IFS(つまり、「ヌル」はここで何を意味するのかだ)(すなわち後の空の値に設定されているIFS=か、IFS=''またはIFS="")すべての(とでは、全くフィールド分割が行われていない$*通常の最初の文字を使用するには、$IFSスペース文字を使用しています)。

Q3:デフォルトのIFS動作にしたい場合は、を使用できますunset IFSIFSこのデフォルト値を明示的に設定する場合は、リテラル文字のスペース、タブ、改行を一重引用符で囲むことができます。ksh93、bash、またはzshでは、を使用できますIFS=$' \t\n'。移植性の高い方法として、ソースファイルにリテラルのタブ文字が含まれないようにするには、次のようにします。

IFS=" $(echo t | tr t \\t)
"

Q4:IFS空の値にread -r line設定lineすると、終了する改行を除く行全体に設定されます。を使用IFS=" "すると、行頭と行末のスペースが削除されます。のデフォルト値ではIFS、タブとスペースは削除されます。


2
Q2は部分的に間違っています。IFSが空の場合、「$ *」はセパレータなしで結合されます。(のように$@、リスト以外のコンテキストではシェル間にいくつかのバリエーションがありますIFS=; var=$@)。IFSが空の場合、単語分割は行われませんが、$ varが空の場合、空の引数の代わりに$ varは引数なしに展開しグロビングが適用されるため、変数を引用する必要があることに注意してください(たとえ
グロビングを

13

Q1。フィールド分割。

フィールド分割は単語分割と同じですか?

はい、両方とも同じ考えを示しています。

Q2:IFSがヌルになるのはいつですか?

設定IFS=''はnullと同じですか、空の文字列とも同じですか?

はい、3つとも同じ意味です。フィールド/単語の分割は実行されません。また、これはフィールドの印刷に影響します(と同様echo "$*")。すべてのフィールドはスペースなしで連結されます。

Q3:(パートa)IFSの設定を解除します。

POSIX仕様では、次読みました

IFSが設定されていない場合、シェルはIFSの値が<space> <tab> <newline>であるかのように動作します

これは次のものとまったく同じです。

を使用するunset IFSと、シェルはIFSがデフォルトのように動作します。

つまり、「フィールド分割」はデフォルトのIFS値とまったく同じになるか、設定されません。
これは、IFSがすべての条件で同じように機能するという意味ではありません。、より具体的なものを実行すると、OldIFS=$IFSVARを設定しますOldIFSはnull、デフォルトではありません。このように、IFSを元に戻すと、IFS=OldIFSIFSはnullに設定され、以前のように未設定のままになりません。気を付けて !!。

Q3:(パートb)IFSを復元します。

IFSの値をデフォルトに戻すにはどうすればよいですか。IFSのデフォルト値を復元するとします。それ、どうやったら出来るの?(より具体的には、どのように<tab><newline>を参照しますか?)

zsh、ksh、およびbash(AFAIK)の場合、IFSは次のようにデフォルト値に設定できます。

IFS=$' \t\n'        # works with zsh, ksh, bash.

完了、他に何も読む必要はありません。

ただし、shのIFSを再設定する必要がある場合、複雑になる可能性があります。

欠点のない(複雑さを除く)簡単に完了できるものを見てみましょう。

1.- IFSの設定を解除します。

できましたunset IFS(上記のQ3パートaをお読みください)。

2.-文字を交換します。

回避策として、tabとnewlineの値を交換すると、IFSの値を簡単に設定でき、同等の方法で機能します。

IFSを<space> <newline> <tab>に設定します

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 not quoted)がポルポースにあるかどうかはわかりません。読み取りにはない第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'

次の行は、var $aが持つすべての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でテストしました。


質問について話していないリンクドキュメントunsetIFSここでの受け入れ答えのコメントで説明したように、。
ILMostro_7
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.