在bash脚本中,我有一个使用rsync的命令:
#!/usr/bin/bash -e
...
parallel rsync --exclude '*to?be?deleted*' ...
--files-from some_file /auto $instance_ip:/somewhere_else/
根据rsync的文档,他们的--exclude
字段具有不同样式的模式匹配。
当我在bash终端中运行它时,它可以正常工作。
但是,在zsh上运行它会给我一个错误,因为zsh试图扩展我试图传入的这个文字字符串:
zsh:1: no matches found: *to?be?deleted*
这应该不发生。为什么zsh甚至首先在我的 bash 脚本中扩展我的全局?我的zsh上是否有一些设置可以设置为使两者的行为方式相同?我不想在zsh中开发并部署到具有bash的环境中,并且必须以不同的方式运行。
我正在使用oh-my-zsh的插件:
plugins=(
git
colored-man-pages
zsh-autosuggestions
zsh-syntax-highlighting
)
具体来说,使用这组命令会失败:
#!/usr/bin/bash -e
find . -name '*filelist' | parallel -j10 rsync --exclude "*to?be?deleted*" testing somewhere_else:/some/where/else
但是使用rsync的命令本身,它不会中断。
答案 0 :(得分:4)
问题是GNU并行实用程序。即使看起来你正在传递一个用参数运行的程序,它实际上做的是连接参数并将它们传递给shell。
此外,Parallel或者运行与您运行的parallel
相同的shell,或者根据SHELL
环境变量选择shell(这是有问题的,因为终端模拟器也使用此环境变量来决定哪个交互式shell运行)。无论哪种方式,这就是为什么它选择zsh而不是sh。你有一个与sh(bash,dash,ksh,...)兼容的shell有同样的问题,但更少见:如果他们不匹配任何东西,sh就会留下模式,所以只要当前目录中没有匹配*to?be?deleted*
的文件,您的脚本就会起作用。
解决方案在手册中给出,但有点难以找到:传递-q
选项。本手册有很长的引用章节,您可以忽略99%的时间:只需传递-q
,除非您打算运行 shell脚本而不是命令。此外,您应该使用命令的完整路径,否则parallel可能会调用shell内置函数甚至函数(如果您的shell是bash)。另外,将SHELL
设置为/bin/sh
,因为即使使用-q
,Parallel也会运行一个shell,并假设它与sh兼容(我认为zsh足够兼容,但我和#39;我不完全确定。)另请参阅a similar question on Unix Stack Exchange
SHELL=/bin/sh parallel -q -j10 "$(command -v rsync)" --exclude "*to?be?deleted*" testing somewhere_else:/some/where/else
(是的,该手册不鼓励您使用-q
,但这是错误的。我之前曾与作者就这一点进行过争论。)
答案 1 :(得分:2)
parallel
正在使用由传递参数组成的字符串启动登录shell的实例。您的bash
脚本在传递参数之前删除引号,因此parallel执行等效的
zsh -c "rsync --exclude *to?be?deleted* testing somewhere_else:/some/where/else"
其中模式不引用。要防止这种情况,请将单个字符串作为参数传递给parallel
:
... | parallel -j10 'rsync --exclude "*to?be?deleted*" testing somewhere_else:/some/where/else'
答案 2 :(得分:1)
GNU并行版本< 20140722使用$SHELL
。更高版本尝试检测从哪个shell GNU Parallel启动,并使用该shell代替。有关检测的详细信息,请参阅man parallel_design
(Which shell to use
)。这里还解释了为什么GNU Parallel总是在shell中运行命令(Always running commands in a shell
)。
如果您不希望shell扩展特殊字符,则可以使用-q
。
但是,该命令必须是一个简单的命令(参见man bash
),没有重定向和没有变量赋值。这个
将引用命令行和参数,以便特殊字符
不是由shell解释的。