重要的是要意识到,实际上是将其扩展foo*到匹配文件名列表的外壳程序,因此几乎没有mv办法自己做。
这里的问题是,当全局不匹配时,某些shell bash(以及大多数其他的Bourne类shell,这些错误行为实际上是Bourne shell在70年代后期引入的)将模式逐字地传递给命令。
因此,在这里,当foo*与任何文件都不匹配时,该外壳将逐字记录foo*文件传递给,而不是中止命令(如pre-Bourne shell和几个现代shell一样)mv,因此基本上要求mv移动名为的文件foo*。
该文件不存在。如果确实如此,则它实际上将与模式匹配,因此mv报告错误。如果采用这种模式foo[xy],则mv可能会意外移动了一个名为foo[xy]而不是foox和fooy文件的文件。
现在,即使在那些没有问题的shell中(Bourne之前的版本,csh,tcsh,fish,zsh,bash -O failglob),您仍然会遇到错误mv foo* ~/bar,但这一次是shell造成的。
如果你想如果没有文件匹配考虑它不是一个错误foo*,并在这种情况下,不动任何东西,你想先建立文件列表(在不使用会导致这样的错误方式nullglob的选择一些shell),然后仅调用mv列表是非空的。
这总比隐藏所有错误mv(就像添加2> /dev/null那样)好,好像mv由于任何其他原因而失败一样,您可能仍想知道为什么。
在zsh中
files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
或使用匿名函数来避免使用临时变量:
() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
zsh是没有Bourne错误并且在glob不匹配(并且nullglob未启用该选项)时不执行命令就报告错误的那些shell中的一个,因此在这里,您可以隐藏zsh的错误并还原stderr for,mv因此您仍然会看到mv错误(如果有),但不会看到有关不匹配的glob的错误:
(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
或者您可以使用zargs它,如果将foo*glob扩展为过多的man文件,也可以避免出现问题。
autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
在ksh93中:
files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
在bash中:
bash没有nullglob仅启用一个glob的语法,并且该failglob选项会取消,nullglob因此您需要执行以下操作:
saved=$(shopt -p nullglob failglob) || true
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
eval "$saved"
或在子外壳中设置选项以保存,必须先保存它们,然后再还原。
(
shopt -s nullglob
shopt -u failglob
files=(foo*)
((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
在 yash
(
set -o nullglob
files=(foo*)
[ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
在 fish
在fish shell中,nullglob行为是该set命令的默认行为,因此仅为:
set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
POSIXly
nullglobPOSIX中没有选项,sh除了位置参数外没有数组。您可以使用一个技巧来检测全局是否匹配:
set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
shift
mv -- "$@" ~/bar/
fi
通过同时使用a foo[*]和foo*glob,我们可以区分没有匹配文件的情况和只有一个文件被恰好被调用的情况foo*(a set -- foo*不能做到)。
更多阅读:
mv foo* ~/bar/ 2> /dev/null?