「while IFS = read ..」では、なぜIFSは効果がないのですか?


12

何か間違っているかもしれませんが、IFDを事前実行/完了リストのコマンドの 1つとして設定してもまったく効果がないことは納得できそうです。以下のスクリプトに示されているすべての例で
は、外側のIFS(構造の外側while)が優先されます。

何が起きてる?この状況でIFSが何をするかについて間違った考えを持っていますか?配列分割の結果は、「expected」列に示されているとおりであると予想しました。


#!/bin/bash
xifs() { echo -n "$(echo -n "$IFS" | xxd -p)"; } # allow for null $IFS 
show() { x=($1) 
         echo -ne "  (${#x[@]})\t |"
         for ((j=0;j<${#x[@]};j++)); do 
           echo -n "${x[j]}|"
         done
         echo -ne "\t"
         xifs "$IFS"; echo
}
data="a  b   c"
echo -e "-----   --  -- \t --------\tactual"
echo -e "outside        \t  IFS    \tinside" 
echo -e "loop           \t Field   \tloop" 
echo -e "IFS     NR  NF \t Split   \tIFS (actual)" 
echo -e "-----   --  -- \t --------\t-----"
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 1'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 2'; show "$REPLY"; done 
IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 3'; show "$REPLY"; done
IFS=" ";      xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 4'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t 5'; show "$REPLY"; done 
IFS=" ";      xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 6'; show "$REPLY"; done
IFS=;         xifs "$IFS"; echo "$data" | while         read; do echo -ne '\t 7'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t 8'; show "$REPLY"; done 
IFS=;         xifs "$IFS"; echo "$data" | while IFS=b   read; do echo -ne '\t 9'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=    read; do echo -ne '\t10'; show "$REPLY"; done
IFS=b;        xifs "$IFS"; echo "$data" | while IFS=" " read; do echo -ne '\t11'; show "$REPLY"; done
echo -e "-----   --  -- \t --------\t-----"

出力:

-----   --  --   --------       actual   
outside           IFS           inside                assigned   
loop             Field          loop    #              inner
IFS     NR  NF   Split          IFS     #  expected    IFS
-----   --  --   --------       -----   #  ---------  --------
20090a   1  (3)  |a|b|c|        20090a  #                              
20090a   2  (3)  |a|b|c|        20090a  #  |a  b   c|  IFS=
20090a   3  (3)  |a|b|c|        20090a  #  |a  |   c|  IFS=b
20       4  (3)  |a|b|c|        20      #                          
20       5  (3)  |a|b|c|        20      #  |a  b   c   IFS=
20       6  (3)  |a|b|c|        20      #  |a  |   c|  IFS=b
         7  (1)  |a  b   c|             #                          
         8  (1)  |a  b   c|             #  |a|b|c|     IFS=" "
         9  (1)  |a  b   c|             #  |a  |   c|  IFS=b
62      10  (2)  |a  |   c|     62      #  |a  b   c|  IFS=
62      11  (2)  |a  |   c|     62      #  |a|b|c|     IFS=" "
-----   --  --   --------       -----      ---------   -------                        

回答:


17

(ごめん、長い説明)

はい、IFS変数in while IFS=" " read; do …は残りのコードには影響しません。

まず、シェルコマンドラインが2種類の変数を備えていることを正確に説明します。

  • シェル変数(シェル内にのみ存在し、シェルに対してローカルです)
  • すべてのプロセスに存在する環境変数。これらは通常、fork()exec()に保存されるため、子プロセスはそれらを継承します。

次のコマンドを呼び出すと:

  A=foo B=bar command

コマンドは、環境(環境)内で実行される変数は、Aに設定されているfooBに設定されていますbar。ただし、このコマンドラインでは、現在のシェル変数AB変更されません

これは次とは異なります。

A=foo; B=bar; command

ここでは、シェル変数ABが定義され、コマンドは環境変数なしで実行されAB定義されます。Aおよびの値はBからアクセスできませんcommand

ただし、一部のシェル変数がexport-edの場合、対応する環境変数はそれぞれのシェル変数と同期されます。例:

export A
export B
A=foo; B=bar; command

このコードでは、両方のシェル変数とシェル環境変数がに設定されているfoobar。環境変数はサブプロセスに継承さcommandれるため、その値にアクセスできます。

元の質問に戻るには:

IFS='a' read

read影響を受けるだけです。実際、この場合、変数のread値は気にしませんIFSIFS次のように、行を分割する(およびいくつかの変数に格納する)ように要求する場合にのみ使用します。

echo "a :  b :    c" | IFS=":" read i j k; \
    printf "i is '%s', j is '%s', k is '%s'" "$i" "$j" "$k"

IFSread引数で呼び出されない限り、使用されません。(編集:これは正確には真実ではありません:空白文字、つまりスペースとタブIFSは、入力行の先頭/末尾では常に無視されます。)


なんて素晴らしい説明でしょう!とても簡単です!何ヶ月もの間、「セミコロンなし」の構文に困惑していました。そして、それは単にローカル変数を意味し、それのケースです!.. rozcietrzewiaczはで私のための経路(ビッグ時間)開かれた他の質問 ...そしてあなただけのケーキの上のアイシングを入れている...私はしてきましたこれで一晩中起きて、それは確かにそのような良い明確な答えのために価値がありました!..ありがとう
。.– Peter.O

うん 私はそれを得る前にその編集コメントを数回読まなければなりませんでした-あなたはそこに存在する空白文字が$IFS入力行の先頭/末尾で削除されると言うことを意味する、私は推測?(どのように機能するか。)
zrajm 14

これを見てください:unix.stackexchange.com/questions/382963/...

IFSの値があるシェルは、まだ入力に単語の分割を行うため、単一の変数を読み込む場合にも重要。だから、例えば、文字を入力するa<tab>bには、read varvarに値を持つになりますa<space>bが、代わりにあなたが持っている場合はIFS='<newline>' read var、その後varの値になりますa<tab>b
ジョンハスコール

8

簡単に言えIFS=<something> read ...、例1でコンストラクトが目に見える効果を持つためには、一度に複数の変数を読み取る必要があります

read例の範囲を逃しています。テストケースのループ内でIFSを変更することはありません。2つ目のIFSが各行のどこで効果を発揮するかを正確に指摘させてください。

 IFS=$' \t\n'; xifs "$IFS"; echo "$data" | while IFS=b   read; do echo ...
                                                      ^      ^
                                                      |      |
                                          from here --'       `- to here :)

シェルで実行されるプログラムと同じです。コマンドラインで(再)定義する変数は、プログラムの実行に影響します。それだけです(エクスポートしないため)。したがって、そのIFSような行で再定義を使用するには、複数の変数readに値を割り当てるように要求する必要があります。これらの例を見てください:

 $ data="a  b   c"
 $ echo "$data" | while           read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a|b||c|
 $ echo "$data" | while IFS=      read A B C; do echo \|$A\|$B\|\|$C\|; done
 |a b c||||
 $ echo "$data" | while IFS='a'   read A B C; do echo \|$A\|$B\|\|$C\|; done
 || b c|||
 $ echo "$data" | while IFS='ab'  read A B C; do echo \|$A\|$B\|\|$C\|; done
 || || c|

1 Gillesから学んだばかりのように、IFS=''1つのフィールドのみを読み取る場合、実際には設定(空白)の利点があります。行の先頭の空白の切り捨てを回避します。


良い..ありがとう...今回は手に入れました。そしてあなたのスケッチが大好きです:)
Peter.O

さて、あなたのコメントを読みましたが、あなたは他の質問でその問題に対する私の答えに気付いていないことがわかります。それは実際には一般的な問題の1つであるため、他の1つを元に戻して削除することができますか?
-rozcietrzewiacz

はい、2つの質問には関連するテーマがありますが、もう1つの質問のタイトルは「なぜIFS= readIFS環境変数を再設定するだけで優先されるのか」です。その場合、コマンドの呼び出し元がローカル変数を設定できることを認識していませんでした。それがその質問に対する答えでした。それはこの質問の主な点に対処するためにさらに進化しましたが、私がすでにこの質問をしていたことに気づいたとき...おそらく2つの質問は2つの質問と同じくらいなsedので、それをそのままにしておくと思います... GoogleからGoogleへのその他のタイトル
Peter.O
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.