看来正常的做法会将 IFS 的设置放在 while 循环之外,以便不为每次迭代重复设置它......这是否只是习惯性的“猴子看,猴子做”风格,就像这只猴子一样,直到我读男人读,或者我在这里错过了一些微妙的(或明显明显的)陷阱?
答案1
陷阱是
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'
。
这个问题的第二部分已合并到这里,所以我从这里删除了相关答案。
答案2
让我们看一个示例,其中包含一些精心设计的输入文本:
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
是最简单的方法(而且,方便地,也是最短的方法)。
1并且,如果read
被捕获信号中断,可能是在陷阱执行时 — 这不是由 POSIX 指定的,而是取决于实践中的 shell。
答案3
除了(已经澄清的) 和 习语之间的作用域差异(每个命令与脚本/shell 范围的变量作用域)之外,最IFS
重要的教训是您失去了领先的while IFS='' read
IFS=''; while read
while IFS=''; read
IFS
和如果 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"
)
答案4
灵感来自尤泽姆的回答
如果你想设置IFS
为实际角色,这对我有用
iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
echo "$line"
done