我有一个包含编号文件的目录,例如 1_foo.txt 2_bar.asc 13_test.png,并且想要使用尽可能简单的 bash 命令将它们移动到单独的目录中(例如 1、2 和 13)。
创建编号目录很容易:
mkdir $(seq 1 15)
我还想出了一条命令来将文件复制到各自的目录中:
seq 15 -1 1 | xargs -I@ mv @_* @
但这样做不起作用,因为当与 xargs 一起使用时,* 会被解释为普通字符,从而出现“mv:未找到文件‘15_*’”之类的错误。
有没有简单的方法可以在 xargs 调用的命令中使用 * 作为通配符?
答案1
你们已经非常接近了。
seq 15 -1 1 | xargs -I@ sh -c 'mv @_* @'
您需要将解释(扩展)延迟到替换发生*
之后@
。(但您已经明白这就是问题所在,对吗?)
我一直建议永远不要将未知文件名(或其他替换字符串)直接嵌入到 shell 命令行中。上面的例子可能相当安全,因为你知道字符串是什么——
15
、、14
……、3
和。但使用上面的例子作为更复杂命令2
的1
模板可能会很危险。更安全的安排是
seq 15 -1 1 | xargs -I@ sh -c 'mv -- "$1"_* "$1"' x-sh @
其中x-sh
是一个半任意字符串,用于标记所调用的 shell 发出的任何错误消息。这等效于我的第一个示例,只不过它不是将字符串(表示为@
)直接嵌入到 shell 命令中,而是通过将 作为参数提供给 shell 来注入它们@
,然后将它们引用为"$1"
。
PS 您建议seq
反向运行该命令(seq 15 -1 1
,生成15
, 14
,...,3
,2
,1
而不是1
,2
,3
,...,14
,15
),但没有人提到它。如果您的文件名是1foo.txt
,2bar.asc
, 和13test.png
等(数字后出现各种字符,而不是总是_
),这将是答案的重要部分。在这种情况下,命令将是mv "$1"* "$1"
(没有_
),如果您1
先这样做,那么命令mv 1* 1
将清除所有10quick*
,11brown*
,12fox*
等文件以及文件1foo*
。但是
seq 1 15 | xargs -I@ sh -c 'mv -- "$1"_* "$1"' x-sh @
应该是安全的。
PPS 该seq
命令不是由 POSIX 指定的。shell 中的括号扩展也不是。可以通过组合以下代码来构建符合 POSIX 的解决方案grawity 对这个问题的回答
和另一个答案经过亚当·卡茨:
i=1
while [ "$i" -le 15 ]
do
mv -- "${i}"_* "$i"
i=$((i+1))
done
PPPS 当您知道文件名以字母数字字符(即字母和数字)开头时,这并不重要,但在更一般的情况下,您应该--
在命令名称和参数之间使用。这可以改善以破折号开头的文件名的处理。告诉 --
命令将参数(文件名)
作为 一个论点。 如果没有它,这样的参数可能会被视为选项字符串。
答案2
不要用xargs
那个。使用for
循环:
for i in $(seq 1 15); do
mv ${i}_* $i
done
更好的方法是使用括号扩展代替seq
:
mkdir {1..15}
for i in {1..15}; do
mv ${i}_* $i
done