neverMind9 Asked: 2019-06-09 12:22:02 +0800 CST2019-06-09 12:22:02 +0800 CST 2019-06-09 12:22:02 +0800 CST 为什么使用“*”通配符时文件移动/复制功能一次只能移动一个文件? 772 function mv1 { mv -n "$1" "targetdir" -v |wc -l ;} mv1 *.png 它只会移动.png它找到的第一个文件,而不是所有文件。 如何使命令适用于与通配符匹配的所有文件? bash shell 3 个回答 Voted Best Answer Gilles 'SO- stop being evil' 2019-06-09T13:08:01+08:002019-06-09T13:08:01+08:00 mv1 *.png首先将通配符模式扩展*.png为匹配的文件名列表,然后将该文件名列表传递给函数。 然后,在函数内部$1意味着:获取函数的第一个参数,将其拆分到包含空格的位置,并用匹配的文件名列表替换任何包含通配符并匹配至少一个文件名的空格分隔部分。听起来很复杂?确实如此,而且这种行为只是偶尔有用,而且经常有问题。这种拆分和匹配行为仅在$1出现在双引号之外时才会发生,因此修复很简单:使用双引号。除非您有充分的理由不这样做,否则请始终在变量替换周围加上双引号。 例如,如果当前目录包含两个文件A* algorithm.png和graph1.png,则作为函数的第一个参数和第二个参数mv1 *.png传递。然后拆分为和。模式匹配,并且不包含通配符。所以函数最终以参数、、和运行。如果您将功能更正为A* algorithm.pnggraph1.png$1A*algorithm.pngA*A* algorithm.pngalgorithm.pngmv-nA* algorithm.pngalgorithm.pngtargetdir-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]}})) } Jarivaa 2019-06-09T12:55:04+08:002019-06-09T12:55:04+08:00 $1是函数的第一个参数,这里是第一个匹配的文件*.png。我想这"$@"就是你想要使用的,而不是$1. neverMind9 2019-06-09T12:22:02+08:002019-06-09T12:22:02+08:00 您将不得不使用mv1 \*.png. 在与函数交互时,Linux 终端不会将星号直接传递给命令,而是选择第一个匹配参数并将其传递给命令。 为了让星号直接通过,需要使用反斜杠转义星号。
mv1 *.png
首先将通配符模式扩展*.png
为匹配的文件名列表,然后将该文件名列表传递给函数。然后,在函数内部
$1
意味着:获取函数的第一个参数,将其拆分到包含空格的位置,并用匹配的文件名列表替换任何包含通配符并匹配至少一个文件名的空格分隔部分。听起来很复杂?确实如此,而且这种行为只是偶尔有用,而且经常有问题。这种拆分和匹配行为仅在$1
出现在双引号之外时才会发生,因此修复很简单:使用双引号。除非您有充分的理由不这样做,否则请始终在变量替换周围加上双引号。例如,如果当前目录包含两个文件
A* algorithm.png
和graph1.png
,则作为函数的第一个参数和第二个参数mv1 *.png
传递。然后拆分为和。模式匹配,并且不包含通配符。所以函数最终以参数、、和运行。如果您将功能更正为A* algorithm.png
graph1.png
$1
A*
algorithm.png
A*
A* algorithm.png
algorithm.png
mv
-n
A* algorithm.png
algorithm.png
targetdir
-v
然后它将正确移动第一个文件。
要处理所有参数,请告诉 shell 处理所有参数,而不仅仅是第一个。您可以使用
"$@"
表示传递给函数的完整参数列表。这几乎是正确的,但如果文件名恰好以字符开头,它仍然会失败
-
,因为mv
会将那个参数视为一个选项。通过--
tomv
告诉它“在此之后没有更多选择”。这是大多数命令都支持的非常常见的约定。剩下的问题是,如果
mv
失败,此函数将返回成功状态,因为管道左侧命令的退出状态被忽略。在 bash(或 ksh)中,您可以使用set -o pipefail
来使管道失败。请注意,设置此选项可能会导致在同一 shell 中运行的其他代码失败,因此您应该在函数中本地设置它,这从 bash 4.4 开始是可能的。在早期版本中,设置
pipefail
会很脆弱,因此最好改为PIPESTATUS
显式检查。$1
是函数的第一个参数,这里是第一个匹配的文件*.png
。我想这"$@"
就是你想要使用的,而不是$1
.您将不得不使用
mv1 \*.png
.在与函数交互时,Linux 终端不会将星号直接传递给命令,而是选择第一个匹配参数并将其传递给命令。
为了让星号直接通过,需要使用反斜杠转义星号。