在`while IFS= read..`中,为什么IFS没有效果?

Pet*_*r.O 14 shell text-processing environment-variables

我可能有一些绝对错误的地方,但对我来说看起来很有说服力,将 IFS 设置为pre-do/done 列表中的命令之一绝对没有效果。
外部 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-----"
Run Code Online (Sandbox Code Playgroud)

输出:

-----   --  --   --------       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=" "
-----   --  --   --------       -----      ---------   -------                        
Run Code Online (Sandbox Code Playgroud)

Sté*_*nez 19

(对不起,解释太长了)

是的,IFS变量 inwhile IFS=" " read; do …对代码的其余部分没有影响。

让我们首先明确 shell 命令行具有两种不同类型的变量:

  • shell 变量(仅存在于 shell 中,并且是 shell 的本地变量)
  • 每个进程都存在的环境变量。这些通常保存在fork()and 上exec(),因此子进程继承它们。

当您使用以下命令调用命令时:

  A=foo B=bar command
Run Code Online (Sandbox Code Playgroud)

该命令在 (environment) 变量A设置为foo并且B设置为的环境中执行bar。但是,使用此命令行,当前shell变量AB被留下不变

这不同于:

A=foo; B=bar; command
Run Code Online (Sandbox Code Playgroud)

在这里,定义了shell 变量AB并且命令在没有环境变量的情况下运行AB定义。值AB来自不可访问command

但是,如果某些 shell 变量被export-ed,则相应的环境变量会与它们各自的 shell 变量同步。例子:

export A
export B
A=foo; B=bar; command
Run Code Online (Sandbox Code Playgroud)

使用此代码,shell变量和 shell环境变量都设置为foobar。由于环境变量由子进程继承,command将能够访问它们的值。

要跳回您的原始问题,请在:

IFS='a' read
Run Code Online (Sandbox Code Playgroud)

read受影响。而事实上,在这种情况下,read并不关心IFS变量的值。它IFS仅在您要求分割行(并存储在多个变量中)时使用,例如:

echo "a :  b :    c" | IFS=":" read i j k; \
    printf "i is '%s', j is '%s', k is '%s'" "$i" "$j" "$k"
Run Code Online (Sandbox Code Playgroud)

IFSread除非使用参数调用,否则不使用。(编辑:这不完全正确:IFS在输入行的开头/结尾总是忽略空格字符,即空格和制表符。)


roz*_*acz 9

简而言之,您必须一次读取多个变量,以便IFS=<something> read ...构造在您的示例1 中产生可见效果。

您错过read了示例中的范围。有没有在你的测试用例环内IFS的修改。请允许我准确指出,第二个 IFS 在你的每一行中都有什么影响:

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

就像在 shell 中执行的任何程序一样。您在命令行(重新)定义的变量会影响程序执行。而是(因为你不出口)。因此,要IFS在这样的行中使用 redefined ,您必须要求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|
Run Code Online (Sandbox Code Playgroud)

1正如我刚刚从 Gilles学到的那样,IFS=''当只读取一个字段时,设置(空白)实际上可能有一个好处:它避免了行首的空格被截断。