在“ while IFS = read ..”中,IFS为什么无效?


12

我可能有绝对错误的提示,但令我信服的是,将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=" "
-----   --  --   --------       -----      ---------   -------                        

Answers:


17

(抱歉,详细说明)

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

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

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

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

  A=foo B=bar command

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

这不同于:

A=foo; B=bar; command

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

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

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

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

要跳回原始问题,请输入:

IFS='a' read

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"

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


很好的解释!太简单了!几个月以来,我一直对这种“无分号”语法感到困惑;这只是一个例子,它意味着一个局部变量!.. rozcietrzewiacz另一个问题上为我(大时间)打开了道路……而您只是锦上添花……我一直在一整夜都花了这么长时间,对于这样良好而明确的答案,这当然是值得的!..谢谢..
Peter.O 2011年

嗯 在获得注释之前,我不得不多次阅读该编辑注释–您的意思是说存在的空格字符$IFS在输入行的开头/结尾处被删除了,我想是吗?(它的工作方式。)
zrajm 2014年


即使在读取单个变量时,IFS的值很重要,因为外壳程序仍会对输入进行字分割。因此,例如,键入字符a<tab>bread var将产生具有价值VAR a<space>b,但相反,如果你有IFS='<newline>' read var那么var值会a<tab>b
John Hascall

8

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

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

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

就像在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|

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


好..谢谢...这次我明白了..我爱你的素描:)
Peter.O 2011年

好的,现在我已经读完您的评论,看到您没有在另一个问题中注意到我对该问题的回答。也许您可以还原另一个并删除它,因为这确实是一个普遍问题?
rozcietrzewiacz 2011年

是的,这两个问题确实有一个相关的主题,但是另一个问题的标题是“为什么IFS= read优先使用它来重新设置IFS环境变量”。因此,我没有意识到,局部变量可以由命令的调用者设置。那就是这个问题的答案。它的确进一步发展为解决了这个问题的要点,但是到我意识到这一点时,我已经问过这个问题了……也许这两个问题与两个sed问题一样,所以我觉得这很有意思。更多标题供Google员工使用。
Peter.O 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.