如何在 xargs 命令中使用通配符?

如何在 xargs 命令中使用通配符?

我有一个包含编号文件的目录,例如 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和。但使用上面的例子作为更复杂命令21模板可能会很危险。更安全的安排是

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,...,321 而不是123,...,1415),但没有人提到它。如果您的文件名是1foo.txt2bar.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

相关内容