为了

为了

我有一个值列表,用“:”分隔,我想一一处理它们。

当分隔符为空格时,没有问题:

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

分配后无法识别关键字。因此,forinIFS=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)必须是:

  1. 命令的第一个词
  2. 除了 case、for 或 in 之外的保留字之一后面的第一个字
  3. case 命令中的第三个字(仅在这种情况下有效)
  4. for 命令中的第三个单词(在这种情况下仅 in 和 do 有效)

这里情况不同。这就是forshell 无法理解并报告错误的原因。

这个问题(关于临时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:冒号分隔的列表。addpathdelpath、等。我在、 、listpath上使用这些功能,以允许搜索离线媒体以及执行一般任务。PATHMANPATHlocatebash

以下是我在 AskUbuntu 上的回答:

我使用 Stephen Collyer 的 bash_path_funcs,早在 2000 年就在 Linux Journal 中进行了描述:

https://www.linuxjournal.com/article/3645 https://www.linuxjournal.com/article/376​​8 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

相关内容