扩展通配符未按预期扩展

4

我有一个名为except.sh的bash脚本,它接收一个文件/目录列表,格式如下:

$ ls
a b c d/
$ ./except.sh b c

当以这种方式调用时,它应该扩展为a d/,即除了给定的名称之外的所有文件/目录。
以下是我尝试实现此功能的方法:
#!/usr/bin/env bash

# enable extended globbing
shopt -s extglob

# set IFS to | so that $* expands correctly
IFS='|'

printf '%s' !("$*")

给定参数b c,最后一行应该扩展为

printf '%s' !(b|c)

导致打印出a d。但令我惊讶的是,
abcd

打印出来了。我做错了什么吗?


1
ls | egrep -v "b|c" 能解决问题吗? - pfnuesel
2个回答

3
问题在于$*被双引号括起来,这意味着它的内容不会被视为模式,就像echo "*"不会展开星号一样。将外部模式与内部带引号的部分组合在一起会自动转义后者,因此!("b|c")被视为!(b\|c)。对不存在的b|c文件的取反自然会扩展到目录中的所有文件。
另一个问题是,IFS被设置为|会破坏扩展的全局模式,因此必须在扩展模式之前重置它。因此,您必须分两步进行扩展:首先计算模式,然后重置IFS并扩展它:
#!/usr/bin/env bash

# enable extended globbing
shopt -s extglob

# temporarily set IFS to | so that $* expands to part an extended pattern
old_ifs=$IFS
IFS='|'
pattern="!($*)"
IFS=$old_ifs

printf '%s' $pattern

+1 这听起来相当合理。我唯一不明白的是为什么将IFS设置为自定义值会导致扩展的globbing不再起作用。 - helpermethod

2

这个问题是由于 IFS 变量被覆盖所导致的(在你覆盖了这个变量之后,bash模式匹配表现的很奇怪,例如,在设置 IFS 之前和之后尝试执行 ls -d !(b|c)),以下应该可以解决:

#!/usr/bin/env bash

# enable extended globbing
shopt -s extglob

PARAMS=$(tr ' ' '|' <<< $*)
printf '%s' !($PARAMS)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接