我可能有一些绝对错误的地方,但它看起来对我来说很有说服力,将 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
(抱歉,解释很长)
是的,IFS
in 中的变量while IFS=" " read; do …
对代码的其余部分没有影响。
我们首先指定 shell 命令行具有两种不同类型的变量:
- shell 变量(仅存在于 shell 内,并且是 shell 的本地变量)
- 环境变量,每个进程都存在。这些通常保存在
fork()
和上exec()
,因此子进程继承它们。
当您使用以下命令调用命令时:
A=foo B=bar command
A
该命令在(环境)变量设置为foo
并B
设置为 的环境中执行bar
。但是使用这个命令行,当前的 shell 变量A
和B
被保留不变。
这不同于:
A=foo; B=bar; command
此处定义了 shell 变量A
和B
,并且命令在没有环境变量A
和B
定义的情况下运行。A
和的值B
无法从 访问command
。
但是,如果某些 shell 变量被export
-ed,则相应的环境变量将与其各自的 shell 变量同步。例子:
export A
export B
A=foo; B=bar; command
通过这段代码,两者壳变量和 shell环境变量设置为foo
和bar
。由于环境变量是由子进程继承的,因此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"
IFS
read
除非使用参数调用,否则不会被使用。 (编辑:这并不完全正确:IFS
输入行开头/结尾处的空白字符(即空格和制表符)始终被忽略。 )
答案2
简单来说,您必须一次读取多个变量使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
,您必须要求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正如我刚刚了解到的来自吉尔斯,当只读取一个字段时设置(空白)实际上可能有一个好处IFS=''
:它避免了行开头的空格被截断。