复制大量文件时出现“参数列表过长”错误


12

我正在使用以下命令:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

而且我得到了错误:

-bash: /bin/cp: Argument list too long

我也尝试过:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

-bash仍然存在:/ bin / ls:参数列表太长

有任何想法吗?


我试图将所有jpg从1个目录复制到另一个目录,但仅复制新文件和已更新的文件。
icelizard

ls并非旨在执行此类操作。使用find
暂停,直到另行通知。

问题不在于ls,而在于shell传递给ls的参数数量。使用vi或任何非内置命令都会出现相同的错误。
克里斯,

ls并非特别为执行此操作而设计:mywiki.wooledge.org/ParsingLs
已暂停,直至另行通知。

的确如此,但是在这种情况下,该错误不是由于ls的解析错误引起的,而是将十亿个参数传递给恰好是ls的新进程。除了不适当地使用ls之外,它还会碰到unix的资源/设计限制。在这种情况下,患者会同时出现胃痛和腿部骨折。
克里斯,

Answers:


19

* .jpg扩展到列表的长度超出外壳程序可以处理的范围。试试这个

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;

我使用find / home / ftpuser1 / public_html / ftparea / -name“ * jpg” -exec cp -uf“ {}” / home / ftpuser2 / public_html / ftparea /并得到以下错误查找:`-exec'缺少参数
icelizard

回答者告诉您,您缺少cp的最后一个参数。仔细检查您的实施。请注意,此答案中缺少“ * .jpg”中的点,这可能导致行为异常(例如,cp名为“ myjpg”的目录)。请注意,这可能有点偏执,但是更安全地使用-type文件指定要复制的内容(防止dirs,符号链接等受到影响)
drAlberT

经过仔细检查,我错过了“ \;” 完成-exec应该执行的命令。傻我!
icelizard

@AlberT:谢谢大家的回首。那是一个错字。答案已更新。
肖恩·钦

并不是说cp无法处理它。外壳不能。
d -_- b

6

系统命令的参数列表有一个最大限制-该限制是特定于发行版的,具体取决于MAX_ARG_PAGES编译内核时的值,并且如果不重新编译内核就无法更改。

由于外壳处理团块的方式,当您使用相同的参数(“ * .jpg”)时,这将影响大多数系统命令。由于glob首先由shell处理,然后发送给命令,因此该命令:

cp -uf *.jpg /targetdir/

与您编写的shell基本相同:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

如果您要处理很多jpeg,那么很快就会变得难以处理。根据命名约定和实际需要处理的文件数,可以一次在目录的另一个子集上运行cp命令:

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

这可能行得通,但确切的效果取决于您可以将文件列表分解为方便的,可混淆的块。

易爆 我喜欢那个词。

某些命令(例如findxargs)可以处理大型文件列表,而无需创建麻烦的参数列表。

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

-exec参数将为find找到的每个文件运行一次命令行的其余部分,将{}替换为找到的每个文件名。由于cp命令一次只能在一个文件上运行,因此参数列表限制不是问题。

由于必须分别处理每个文件,因此速度可能很慢。使用xargs可以提供更有效的解决方案:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargs可以获取find所提供的完整文件列表,并将其分解为可管理大小的参数列表,然后在每个子列表上运行cp

当然,也可以重新编译内核,为设置更大的值MAX_ARG_PAGES。但是重新编译内核比我愿意在这个答案中解释的工作还要多。


我不知道为什么这被否决了。这是唯一的答案,似乎正在解释为什么会这样。也许是因为您不建议使用xargs作为优化?
克里斯,

在xargs解决方案中添加了,但是我仍然担心下降投票是由于我的详细信息公然出错,而且没人愿意告诉我它是什么。:(
goldPseudo

xargs似乎效率更高,因为命令调用的结果数量要少得多。就我而言,args使用-exec文件时,性能会提高6到12倍,而使用解决方案时,效率会提高。
2013年

3

发生这种情况是因为您的通配符表达式(*.jpg)展开时超出了命令行参数长度限制(可能是因为之下有很多.jpg文件/home/ftpuser/public_html/ftparea)。

有几种方法可以避免这种限制,例如使用findxargs。请参阅本文,以获取有关如何执行此操作的更多详细信息。


+1为主题提供了良好的外部资源。
viam0Zah

3

正如GoldPseudo所评论的那样,可以将多少个参数传递给所生成的进程是有限制的。有关该参数的详细说明,请参见他的答案。

您可以通过不向进程传递过多的参数或减少传递的参数数量来避免该问题。

在这种情况下,shell中的for循环,find和ls,grep和while循环都做同样的事情-

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

所有的程序都有一个读取目录的程序(shell本身,find和ls),以及一个不同的程序,该程序实际上每次执行都带有一个参数,并遍历整个命令列表。

现在,这将变得很慢,因为需要对与* .jpg模式匹配的每个文件执行rm分叉并执行。

这就是xargs发挥作用的地方。xargs接受标准输入,每N行(对于freebsd默认为5000行),它产生一个带有N个参数的程序。xargs是上述循环的优化,因为您只需要派生1 / N程序即可遍历从命令行读取自变量的整个文件集。



1

'*'全局文件扩展到太多文件名。请改用find / home / ftpuser / public_html -name'* .jpg'。


查找并回显*会得到相同的输出-此处的关键是使用xargs而不只是将所有10亿命令行参数传递给shell尝试分叉的命令。
克里斯,克里斯,

如果文件太多,echo *将失败,但是查找将成功。另外,将find -exec与+一起使用等效于使用xargs。(不过,并非所有人都能找到支持+)
William Pursell,09年

1

使用+选项find -exec将大大加快操作速度。

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

+选项必须{}是最后一个参数,因此请使用-t /your/destination(或--target-directory=/your/destination)选项cp使其生效。

来自man find

-exec命令{} +

          This  variant  of the -exec action runs the specified command on  
          the selected files, but the command line is built  by  appending  
          each  selected file name at the end; the total number of invoca  
          tions of the command will  be  much  less  than  the  number  of  
          matched  files.   The command line is built in much the same way  
          that xargs builds its command lines.  Only one instance of  ‘{}’  
          is  allowed  within the command.  The command is executed in the  
          starting directory.

编辑:cp的重新排列的参数


我发现:缺少-exec的参数/ home / ftpuser1 / public_html / ftparea / -name'* jpg'-exec cp -uf“ {}” / home / ftpuser2 / public_html / ftparea /
icelizard

我重新排列了参数cp以修复该错误。
暂停,直到另行通知。

1

听起来您*.jpg在该目录中的文件太多,无法一次将它们全部放在命令行中。您可以尝试:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

您可能需要检查man xargs实现,以查看该-I开关对于您的系统是否正确。

实际上,您是否真的打算将这些文件复制到它们已经存在的相同位置?


抱歉,这是两个不同的目录,应该是ftpuser1和ftpuser2
icelizard

刚刚尝试过:ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea /仍然得到-bash:/ bin / ls:参数列表过长
icelizard,2009年

哦,您说得很对,当然ls会有同样的问题!我已更改为find不会。
2009年

0

转到文件夹

cd /home/ftpuser1/public_html/

并执行以下命令:

cp -R ftparea/ /home/ftpuser2/public_html/

这样,如果文件夹“ ftparea”具有子文件夹,则如果只希望从其中获得“ * .jpg”文件,则可能会产生负面影响,但是如果没有子文件夹,则此方法肯定比使用该方法快得多。使用find和xargs

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.