为什么 printf 在打印脚本结果时忽略 IFS?

为什么 printf 在打印脚本结果时忽略 IFS?

这是一个后续问题发布在SO上。

我编写了一个名为的脚本except,它应该打印除像这样给出的文件名/目录之外的所有文件名/目录。

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

到目前为止我写的脚本看起来像这样

shopt -s extglob
shopt -s nullglob

old_ifs=$IFS
IFS='|'
matches="!($*)"
IFS=' '

printf '%s' $matches
IFS=$old_ifs

这或多或少有效,但主要问题是 printf 似乎不尊重IFS设置为(空白)。

它只是打印它们,中间没有空格。如果我改用 echo,它就可以工作(除了添加的换行符)。但出于兼容性原因,我想使用 printf 来完成此任务。我怎样才能实现这个目标?我究竟做错了什么?

我还尝试使用%q修饰符,但也没有产生所需的输出。

printf '%q' $matches

答案1

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

这是行不通的,至少不可靠,因为"except"无法决定 shell 在将其输出传递给命令之前如何分割(并进一步扩展)其输出rm

要回答您的问题, printf 不使用$IFS.唯一使用 IFS 的命令是read.除此之外,shell 使用 $IFS 进行分词并连接 中的位置参数"$*"

您可能想要:

 printf '%s ' $matches

或者

 printf '%s\n' $matches

但话又说回来,在

 rm $(except a b)

except解释该命令行的 shell 将根据自己的情况分割输出$IFS(默认为空格、制表符或换行符,这就是为什么使用空格或\n以上没有区别),并且还会扩展其中的通配符。

a因此,如果除了和之外的文件b"this file.txt"**a**,except将输出"this file.txt **a** ",并且您最终将尝试删除this,file.txta(因为将扩展到其名称中的**a**所有文件)。a

如果你想用空格连接匹配而不用尾随空格,你可以这样做:

IFS=
set -- $matches
IFS=' '
matches="$*"

相关内容