有没有办法让“mv”静默失败?

Jon*_*nik 76 bash mv

mv foo* ~/bar/如果没有匹配的文件,则类似命令会在 stderr 中生成此消息foo*

mv: cannot stat `foo*': No such file or directory
Run Code Online (Sandbox Code Playgroud)

但是,在我正在处理的脚本中,这种情况完全没有问题,我想从我们的日志中省略该消息。

有没有什么好的方法可以让你mv保持安静,即使什么都没有移动?

小智 64

你在找这个吗?

$ mv  file dir/
mv: cannot stat ‘file’: No such file or directory
$ mv  file dir/ 2>/dev/null
# <---- Silent ----->
Run Code Online (Sandbox Code Playgroud)

  • 请注意,返回值仍然 != 0,因此任何 `set -e`(应该在 **every** shell 脚本中使用)都将失败。你可以添加一个`|| true` 以停用对单个命令的检查。 (32认同)
  • @reto `set -e` 不应该在每个 shell 脚本中使用,在许多情况下,它会使错误管理比没有时更复杂。 (13认同)
  • @ChrisDown 我明白你的观点。通常是在两害之间做出选择。但如果有疑问,我更喜欢失败的成功运行,而不是成功的失败。(在客户/用户看到之前不会注意到)。对于初学者来说,Shell 脚本是一个很大的雷区,很容易错过错误,导致您的脚本变得混乱! (3认同)

Mir*_*kár 17

实际上,我不认为静音mv是一种好方法(请记住,它可能还会报告您可能感兴趣的其他事情……例如丢失~/bar)。您只想在 glob 表达式不返回结果的情况下将其静音。事实上,根本不执行它。

[ -n "$(shopt -s nullglob; echo foo*)" ] && mv foo* ~/bar/
Run Code Online (Sandbox Code Playgroud)

看起来不是很吸引人,只能在bash.

或者

[ 'foo*' = "$(echo foo*)" ] || mv foo* ~/bar/
Run Code Online (Sandbox Code Playgroud)

只有除非你是在bashnullglob一套。不过,您要付出 3 倍重复 glob 模式的代价。

  • 第二种形式的小问题:当名为“foo*”的文件是当前目录中唯一与 glob“foo*”匹配的文件时,它无法移动该文件。这可以通过与字面意思不匹配的全局表达式来解决,很难。例如 `[ 'fo[o]*' = "$(echo fo[o]*)" ] || mv fo[o]* ~/bar/`. (2认同)

poi*_*ige 13

find . -maxdepth 1 -name 'foo*' -type f -print0 | xargs -0r mv -t ~/bar/

— GNUmv有很好的“目标优先”选项 ( -t),xargs如果根本没有输入,可以跳过运行它的命令 ( -r)。使用-print0-0相应地确保当文件名包含空格和其他“有趣”的东西时不会出现混乱。

  • 请注意,`-maxdepth`、`-print0`、`-0` 和`-r` 也是GNU 扩展(尽管现在可以在其他实现中找到其中一些)。 (2认同)
  • Poige,我们的很多用户不使用 Linux。指出答案的哪些部分不可移植是这里的标准做法,而且非常有帮助。该站点名为“**Unix** &amp; Linux”,许多人使用某种形式的 BSD 或 Unix 或 AIX 或 macOS 或嵌入式系统。不要把它当个人。 (2认同)

Sté*_*las 8

重要的是要意识到实际上是 shell 将其扩展foo*为匹配的文件名列表,因此它自己几乎mv无能为力。

这里的问题是,当 glob 不匹配时,一些 shell bash(以及大多数其他类似 Bourne 的 shell,这种错误行为实际上是由 Bourne shell 在 70 年代后期引入的)会将模式逐字传递给命令。

所以在这里,当foo*不匹配任何文件时,而不是中止命令(如前 Bourne shell 和几个现代 shell 所做的那样),shell 将逐字foo*文件传递给mv,因此基本上要求mv移动名为foo*.

那个文件不存在。如果是这样,它实际上会与模式匹配,因此会mv报告错误。如果该模式被foo[xy]替换,则mv可能会意外移动一个名为foo[xy]而不是fooxandfooy文件的文件。

现在,即使在那些没有这个问题的 shell 中(pre-Bourne、csh、tcsh、fish、zsh、bash -O failglob),你仍然会在 上得到一个错误mv foo* ~/bar,但这次是由 shell。

如果您想在没有文件匹配的foo*情况下将其视为错误并且在这种情况下不移动任何内容,则您需要首先构建文件列表(以不会导致错误的方式,例如使用nullglob选项一些shell),然后唯一调用的mv是列表非空。

这比隐藏(添加会)的所有错误更好,好像由于任何其他原因而失败,您可能仍然想知道原因。mv2> /dev/nullmv

在 zsh

files=(foo*(N)) # where the N glob qualifier activates nullglob for that glob
(($#files == 0)) || mv -- $files ~/bar/
Run Code Online (Sandbox Code Playgroud)

或者使用匿名函数来避免使用临时变量:

() { (($# == 0)) || mv -- "$@" ~/bar/; } foo*(N)
Run Code Online (Sandbox Code Playgroud)

zsh是那些没有 Bourne 错误的 shell 之一,并且在 glob 不匹配(并且该nullglob选项尚未启用)时报告错误而不执行命令,因此在这里,您可以隐藏zsh错误并恢复stderr formv因此您仍然会看到mv错误(如果有),但不会看到有关不匹配的 glob 的错误:

(mv 2>&3 foo* ~/bar/) 3>&2 2>&-
Run Code Online (Sandbox Code Playgroud)

或者zargs,如果foo*glob 扩展为 man 文件,您也可以使用which 也可以避免问题。

autoload zargs # best in ~/.zshrc
zargs -r -- foo* -- mv -t ~/bar # here assuming GNU mv for its -t option
Run Code Online (Sandbox Code Playgroud)

在 ksh93 中:

files=(~(N)foo*)
((${#files[#]} == 0)) || mv -- "${files[@]}" ~/bar/
Run Code Online (Sandbox Code Playgroud)

在 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"
Run Code Online (Sandbox Code Playgroud)

或在子shell中设置选项以保存必须在之前保存它们并在之后恢复它们。

(
  shopt -s nullglob
  shopt -u failglob
  files=(foo*)
  ((${#files[@]} == 0)) || mv -- "${files[@]}" ~/bar/
)
Run Code Online (Sandbox Code Playgroud)

yash

(
  set -o nullglob
  files=(foo*)
  [ "${#files[@]}" -eq 0 ] || mv -- "${files[@]}" ~/bar/
)
Run Code Online (Sandbox Code Playgroud)

fish

在 fish shell 中, nullglob 行为是set命令的默认行为,因此它只是:

set files foo*
count $files > /dev/null; and mv -- $files ~/bar/
Run Code Online (Sandbox Code Playgroud)

POSIXly

nullglobPOSIX 中没有选项,sh除了位置参数之外没有数组。您可以使用一个技巧来检测 glob 是否匹配:

set -- foo[*] foo*
if [ "$1$2" != 'foo[*]foo*' ]; then
  shift
  mv -- "$@" ~/bar/
fi
Run Code Online (Sandbox Code Playgroud)

通过同时使用 afoo[*]foo*glob,我们可以区分没有匹配文件的情况和有一个文件恰好被调用的情况foo*(aset -- foo*不能这样做)。

更多阅读: