为什么使用“*”通配符时文件移动/复制功能一次只能移动一个文件?

为什么使用“*”通配符时文件移动/复制功能一次只能移动一个文件?
function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}
mv1 *.png

它只会移动.png找到的第一个文件,而不是所有文件。

如何使该命令应用于与通配符匹配的所有文件?

答案1

mv1 *.png首先将通配符模式扩展*.png为匹配文件名列表,然后将该文件名列表传递给函数。

然后,在函数内部$1意味着:将第一个参数传递给函数,在包含空格的地方将其拆分,并替换任何包含通配符的空格分隔部分,并通过匹配文件名列表匹配至少一个文件名。听起来很复杂?确实如此,而且这种行为只是偶尔有用,而且经常会出现问题。这种拆分和匹配行为仅发生$1在双引号之外,因此修复很简单:使用双引号。始终在变量替换两边加上双引号除非你有充分的理由不这样做。

例如,如果当前目录包含两个文件A* algorithm.pnggraph1.png,则作为第一个参数mv1 *.png传递A* algorithm.png给函数,并graph1.png作为第二个参数传递。然后$1分为A*algorithm.png。该模式A*匹配A* algorithm.png, 并且algorithm.png不包含通配符。因此该函数最终mv以参数-nA* algorithm.pngalgorithm.png和运行targetdir-v如果您将函数更正为

function mv1 { mv -n "$1" "targetdir" -v |wc -l ;}

那么它将正确移动第一个文件。

处理全部参数,告诉 shell 处理所有参数而不仅仅是第一个参数。您可以使用"$@"来表示传递给函数的参数的完整列表。

function mv1 { mv -n "$@" "targetdir" -v |wc -l ;}

这几乎是正确的,但如果文件名恰好以字符 开头-,它仍然会失败,因为mv会将该参数视为选项。传递--tomv告诉它“此后没有更多选项”。这是大多数命令都支持的非常常见的约定。

function mv1 { mv -n -v -- "$@" "targetdir" |wc -l ;}

剩下的问题是,如果mv失败,该函数将返回成功状态,因为管道左侧命令的退出状态被忽略。在 bash(或 ksh)中,您可以使用set -o pipefail使管道失败。请注意,设置此选项可能会导致同一 shell 中运行的其他代码失败,因此您应该在函数中本地设置它,这从 bash 4.4 开始是可能的。

function mv1 {
  local -
  set -o pipefail
  mv -n -v -- "$@" "targetdir" | wc -l
}

在早期版本中,设置pipefail会很脆弱,因此最好PIPESTATUS明确检查。

function mv1 {
  mv -n -v -- "$@" "targetdir" | wc -l
  ((!${PIPESTATUS[0] && !${PIPESTATUS[1]}}))
}

答案2

$1是函数的第一个参数,这里是第一个匹配的文件*.png。我想这"$@"就是您想要使用的而不是$1.

答案3

你必须使用mv1 \*.png.

与函数交互时,Linux 终端不会直接将星号传递给命令,而是选择第一个匹配的参数并将其传递给命令。

为了让星号直接通过,需要使用反斜杠转义星号。

相关内容