除了基本的*
,?
和[...]
模式之外,Bash shell还提供了扩展模式匹配运算符,例如!(pattern-list)
(“匹配除了给定的一个之外的所有运算符模式“)。需要设置extglob
shell选项以使用它们。一个例子:
~$ mkdir test ; cd test ; touch file1 file2 file3
~/test$ echo *
file1 file2 file3
~/test$ shopt -s extglob # make sure extglob is set
~/test$ echo !(file2)
file1 file3
如果我将shell表达式传递给在子shell中执行它的程序,则运算符会导致错误。这是一个直接运行子shell的测试(这里我从另一个目录执行以确保不会过早地进行扩展):
~/test$ cd ..
~$ bash -c "cd test ; echo *"
file1 file2 file3
~$ bash -c "cd test ; echo !(file2)" # expected output: file1 file3
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `cd test ; echo !(file2)'
我尝试了各种各样的逃避,但我没想到的任何东西都能正常工作。我还怀疑extglob
未设置在子shell中,但事实并非如此:
~$ bash -c "shopt -s extglob ; cd test ; echo !(file2)"
bash: -c: line 0: syntax error near unexpected token `('
bash: -c: line 0: `cd test ; echo !(file2)'
任何解决方案都赞赏!
答案 0 :(得分:4)
bash在执行之前解析每一行,因此当bash验证globbing模式语法时,“shopt -s \ textglob”不会生效。无法在同一行上启用该选项。这就是为什么“bash -O extglob -c'xyz'”解决方案(来自Randy Proctor)的工作原理并且是必需的。
答案 1 :(得分:3)
$ bash -O extglob -c 'echo !(file2)' file1 file3
答案 2 :(得分:3)
这是另一种方法,如果你想避免eval
,你需要能够在子shell中打开和关闭 extglob 。只需将您的模式放在变量中:
bash -c 'shopt -s extglob; cd test; patt="!(file2)"; echo $patt; shopt -u extglob; echo $patt'
给出了这个输出:
file1 file3
!(file2)
证明 extglob 已设置并取消设置。如果第一个echo
在$patt
附近有引号,它就会像第二个echo
(可能应该有引号)一样吐出模式。
答案 3 :(得分:1)
好吧,我对 \ textglob 没有任何实际经验,但我可以通过将echo
包裹在eval
中来实现它:
$ bash -c 'shopt -s extglob ; cd test ; eval "echo !(file2)"'
file1 file3