find + xargs:参数行太长

find + xargs:参数行太长

我有如下一行:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

但我收到以下错误:

xargs: argument line too long

我很困惑。难道使用 Shouldxargs正好可以帮助解决这个问题吗?

笔记:我知道我可以-exec在技术上使用 find ,但我想了解为什么上述失败,因为我的理解是xargs应该知道如何将输入分割成可管理的大小以适应其运行的参数。这不是真的吗?

这一切都与 zsh 相关。

答案1

一方面,该-i开关已被弃用:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

所以当我将你的命令更改为这个时,它起作用了:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

例子

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

用于-I{}

自从运行此命令构造后,不应使用此方法:

$ find -print0 ... | xargs -I{} -0 ...

隐式地将这些开关打开为xargs-x-L 1。配置以便它调用您希望它以单一方式运行文件的命令-L 1xargs

因此,这违背了此处使用的目的,xargs因为如果您给它 1000 个文件,它将运行该mv命令 1000 次。

那么我应该使用哪种方法呢?

您可以使用 xargs 来完成此操作,如下所示:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

或者只是找到完成这一切:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +

答案2

该选项-i采用可选参数。由于您在后面放置了一个空格-i,因此该选项没有参数-i,因此后续的选项-0不是xargs6 个操作数中的第二个,而是 6 个操作数中的第二个{} -0 mv -t /some/path {}

仅使用选项 时-i,xargs 需要换行符分隔的文件名列表。由于输入中可能没有换行符,因此 xargs 收到了看​​起来很大的文件名(带有嵌入的空字节,但 xargs 没有检查)。包含整个输出的单个字符串find比最大命令行长度长,因此出现错误“命令行太长”。

您的命令将使用-i{}而不是-i {}.或者,您可以使用-I {}:-I与 类似-i,但采用强制参数,因此传递给 的下一个参数xargs将用作选项的参数-I。然后后面的参数 is-0被解释为选项,依此类推。

但是,您-I {}根本不应该使用。使用-I具有三个作用:

  • -I关闭报价处理,这-0已经完成了。
  • -I更改要替换的字符串,但{}为默认值。
  • -I导致为每个输入记录单独执行该命令,这在这里是无用的,因为您的命令 ( mv -t) 专门用于处理每次调用的多个文件。

要么放弃-I,要么-i完全

find /foo/bar -name '*.mp4' -print0 | xargs -0 mv -t /some/path {}

或删除 xargs 并使用-exec

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +

答案3

尝试使用 bash for 循环:

for FILE in *.mp4 ; do rm $FILE ; done

或者如果您想看看发生了什么:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done

答案4

如果您在使用时看到此内容鱼壳
这与鱼如何扩展替换串有关{}

如果您使用的是 Fish,则需要转义替换字符串\{\}

| xargs -I \{\} echo \{\}

或使用不同的替换字符串

| xargs -I ! echo !

相关内容