Answers:
陷阱是
IFS=; while read..
设置IFS
为外循环整个shell环境,而
while IFS= read
仅在read
调用时重新定义它(Bourne shell中除外)。您可以检查是否像
while IFS= read xxx; ... done
然后在这样的循环之后,echo "blabalbla $IFS ooooooo"
打印
blabalbla
ooooooo
而之后
IFS=; read xxx; ... done
重新定义的IFS
支柱:现在echo "blabalbla $IFS ooooooo"
打印
blabalbla ooooooo
因此,如果您使用第二种形式,则必须记住要重置:IFS=$' \t\n'
。
该问题的第二部分已在此处合并,因此我从此处删除了相关答案。
while
没有太大意义- 以该分号while
结尾的条件,因此没有实际的循环... read
成为单元素循环中的第一个命令... ?那do
那..呢?
while
条件中(在之前do
)有几个命令。
IFS=
行得通,但IFS=X
没有...(或者也许我对此做了一段时间..需要喝咖啡休息时间:)
让我们看一个带有精心设计的输入文本的示例:
text=' hello world\
foo\bar'
这是两行,第一行以空格开头,以反斜杠结尾。首先,让我们看一下发生的情况,而无需采取任何预防措施read
(但printf '%s\n' "$text"
要谨慎打印,$text
而不会产生扩展风险)。(下面$
是shell提示符。)
$ printf '%s\n' "$text" |
while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]
read
吃了反斜杠:backslash-newline导致换行符被忽略,而backslash-anything则忽略第一个反斜杠。为了避免对反斜杠进行特殊处理,我们使用read -r
。
$ printf '%s\n' "$text" |
while read -r line; do printf '%s\n' "[$line]"; done
[hello world\]
[foo\bar]
更好,我们按预期有两行。这两行几乎包含所需的内容:hello
和之间的双精度空格world
已保留,因为它位于line
变量内。另一方面,最初的空间被吃光了。这是因为read
读取的单词与传递给它的变量一样多,除了最后一个变量包含该行的其余部分,但它仍以第一个单词开头,即初始空格被丢弃。
因此,为了按字面意义阅读每一行,我们需要确保没有进行分词。为此,我们将IFS
变量设置为空值。
$ printf '%s\n' "$text" |
while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello world\]
[foo\bar]
请注意我们如何IFS
专门针对read
内置时间进行设置。的IFS= read -r line
设置环境变量IFS
专门为执行(为空值)read
。这是一般简单命令语法的一个实例:变量分配的序列(可能为空),后跟命令名称及其参数(同样,您可以在任何时候抛出重定向)。由于read
是内置变量,因此该变量永远不会最终出现在外部进程的环境中。但是$IFS
,只要read
执行¹,值就是我们在其中分配的值。请注意,这read
不是一个特殊的内置变量,因此分配仅在其持续时间内有效。
因此,我们注意不要更改IFS
可能依赖它的其他指令的值。无论周围的代码IFS
最初设置为什么,该代码都将起作用,并且如果循环内的代码依赖,也不会造成任何麻烦IFS
。
与此代码段对比,该代码段以冒号分隔的路径查找文件。从文件中读取文件名列表,每行一个文件名。
IFS=":"; set -f
while IFS= read -r name; do
for dir in $PATH; do
## At this point, "$IFS" is still ":"
if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
done
done <filenames.txt
如果循环为while IFS=; read -r name; do …
,则for dir in $PATH
不会拆分$PATH
为冒号分隔的组件。如果代码为IFS=; while read …
,则在循环主体中IFS
未设置为会更加明显:
。
当然,有可能IFS
在执行之后恢复的值read
。但这需要知道先前的值,这是额外的工作。IFS= read
是最简单的方法(也是最简便的方法)。
¹ 而且,如果read
被捕获的信号中断,则可能是在执行陷阱时-POSIX并未指定,实际上取决于外壳。
while IFS= read
(。之后没有分号=
)不是。while
或of IFS
或read
。的特殊形式。该构造是通用的:即。anyvar=anyvalue anycommand
。缺少;
after设置anyvar
使anyvar
local的范围为anycommand
.. while--do / done循环与.local的范围无关any_var
。
除了(和已经阐明的),和习惯用法IFS
之间的作用域区别(每个命令vs脚本/ shell范围的变量作用域)之外,总结课是,如果IFS变量,您将丢失输入行的前导和尾随空格设置为(包含)空格。while IFS='' read
IFS=''; while read
while IFS=''; read
IFS
如果正在处理文件路径,则可能会导致非常严重的后果。
因此,将IFS变量设置为空字符串不是什么坏主意,因为它可以确保不会删除行的开头和结尾空格。
另请参阅:Bash,使用IFS从文件逐行读取
(
shopt -s nullglob
touch ' file with spaces '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)
while IFS=X read
不会在分裂X
,但是while IFS=X; read
会...