我有一个值列表,用“:”分隔,我想一一处理它们。
当分隔符为空格时,没有问题:
nuclear@korhal:~$ for a in 720 500 560 130; do echo $a; done
720
500
560
130
但是将 IFS (内部字段分隔符)设置为 : 后,奇怪的事情开始发生:
nuclear@korhal:~$ IFS=":" for a in 720:500:560:130; do echo $a; done;
bash: syntax error near unexpected token `do'
如果我在设置 IFS 时跳过所有分号:
nuclear@korhal:~$ IFS=":" for a in 720:500:560:130 do echo $a done;
Command 'for' not found, did you mean:
command 'vor' from deb vor (0.5.8-1)
command 'fop' from deb fop (1:2.5-1)
command 'tor' from deb tor (0.4.4.5-1)
command 'forw' from deb mailutils-mh (1:3.9-3.2)
command 'forw' from deb mmh (0.4-2)
command 'forw' from deb nmh (1.7.1-7)
command 'sor' from deb pccts (1.33MR33-6build1)
command 'form' from deb form (4.2.1+git20200217-1)
command 'fox' from deb objcryst-fox (1.9.6.0-2.2)
command 'fort' from deb fort-validator (1.4.0-1)
command 'oor' from deb openoverlayrouter (1.3.0+ds1-3)
Try: sudo apt install <deb name>
Bash 根本不识别 for 命令。如果在这种情况下没有设置 IFS,它将显示提示,因为它需要更多输出(正常行为)
当 IFS 设置为自定义字符时会发生什么?为什么 for 循环不起作用?
我正在使用 Kubuntu 20.10 Bash 版本 5.0.17
答案1
分配后无法识别关键字。因此,for
inIFS=blah for ...
只是运行一个名为 的常规命令(for
如果有的话):
$ cat > ./for
#!/bin/sh
echo script for
$ chmod +x ./for
$ PATH=$PATH:.
$ for x in a b c
> ^C
$ foo=bar for x in a b c
script for
但由于 Bash 在运行之前会解析整个输入行,因此该关键字do
会在此之前导致语法错误。
这与用重定向代替赋值类似: 我可以在复合命令之前指定重定向输入吗?并且还看到为什么不能反转 while 循环的输入重定向运算符的顺序?有关如何定义语法的详细信息。
另请参阅:如何在 bash for 循环中使用临时环境变量?
我的 Zsh 更严格:
$ zsh -c 'foo=bar for x in a b c'
zsh:1: parse error near `for'
但 Zsh 确实允许在复合命令之前进行重定向。这将三行输出到test.txt
:
$ zsh -c '> test.txt for x in a b c ; do echo $x; done '
此外,请注意,它IFS
不会用于拆分静态字符串,如720:500:560:130
,分词仅适用于扩展。所以:
$ IFS=":"
$ for a in 720:500:560:130; do echo "$a"; done;
720:500:560:130
但,
$ IFS=":"
$ s=720:500:560:130
$ for a in $s; do echo "$a"; done;
720
500
560
130
答案2
为了
作为POSIX 要求某些单词(包括for
)必须是:
- 命令的第一个词
- 除了 case、for 或 in 之外的保留字之一后面的第一个字
- case 命令中的第三个字(仅在这种情况下有效)
- for 命令中的第三个单词(在这种情况下仅 in 和 do 有效)
这里情况不同。这就是for
shell 无法理解并报告错误的原因。
这个问题(关于临时IFS)与什么时候可以使用临时 IFS 进行字段拆分?。但该答案仅涵盖简单命令(这是一个复合命令)。
此外:
IFS
IFS 的值只影响扩展(展开变量)。
IFS 的值必须在前面的命令中设置后才会生效:
$ a="one:two:three"
$ echo $a
one:two:three
$ (
> IFS=":"
> echo $a
> )
one two three
$ ( IFS=":"; echo $a )
one two three
但不是:
$ ( IFS=":" echo $a )
one:two:three
为此,您需要延迟命令的执行:
$ ( IFS=":" eval echo '$a' )
one two three
因此,对于您的脚本,您需要执行以下操作:
b="720:500:560:130"
( IFS=":";
for a in $b ;
do echo "$a" ;
done
)
请注意,由于$b
不加引号地展开,因此任何 glob 字符(*、[、? 和其他带有 extglob 集的字符)也将被展开。也许生成一个文件列表而不是仅一个。
答案3
与其与 IFS 魔法作斗争,不如使用 Stephen Collyer 的“路径”函数。它们允许使用一种新的数据类型bash
:冒号分隔的列表。addpath
、delpath
、等。我在、 、listpath
上使用这些功能,以允许搜索离线媒体以及执行一般任务。PATH
MANPATH
locate
bash
以下是我在 AskUbuntu 上的回答:
我使用 Stephen Collyer 的 bash_path_funcs,早在 2000 年就在 Linux Journal 中进行了描述:
https://www.linuxjournal.com/article/3645 https://www.linuxjournal.com/article/3768 https://www.linuxjournal.com/article/3935
addpath
仅当路径中最初不存在该条目时,该函数才会将其添加到路径中。delpath -n
从路径中删除所有不存在的目录。
您可以pathfunc.tgz
从以下位置获取该文件 https://web.archive.org/web/20061210054813/http://www.netspinner.co.uk:80/Downloads/pathfunc.tgz