`while IFS = read..`でIFSが何も効果がないのはなぜですか?

`while IFS = read..`でIFSが何も効果がないのはなぜですか?

何かが間違っている可能性がありますが、IFSを次のいずれかに設定すると説得力があるようです。注文する事前完了/完了リストにはまったく影響しません。
外部IFS(while構成外)は、以下のスクリプトに示されているすべての例で支配的です。

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


#!/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=" "
-----   --  --   --------       -----      ---------   -------                        

答え1

(説明が長くてすみません)

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

まず、シェルコマンドラインに2つの異なる種類の変数があることを指定します。

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

以下を使用してコマンドを呼び出す場合:

  A=foo B=bar command

Afooコマンドは、(環境)変数がにB設定されている環境で実行されますbar。しかし、このコマンドラインを使用すると、現在のシェル変数AB絶え間ない

これは以下とは異なります。

A=foo; B=bar; command

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

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

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

このコードを使用すると、両方シェル変数とシェル環境変数fooはとに設定されますbar。環境変数は子プロセスに継承されるため、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入力行の先頭/末尾にある空白文字(スペースやタブなど)は常に無視されます。 )

答え2

簡単に言えば、一度に複数の変数を読み取る必要があります。example1IFS=<something> read ...で設定が目立つ効果を持つようにします。

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今学んだとおりザイルズから、1つのフィールドのみを読み取るとき(スペース)に設定すると、実際に利点がある可能性がありますIFS=''。つまり、行の先頭で空白が切り取られるのを防ぎます。

関連情報