rm,cp,mv命令的参数列表过长错误


629

我在UNIX的目录下有数百个PDF。PDF的名称确实很长(约60个字符)。

当我尝试使用以下命令一起删除所有PDF时:

rm -f *.pdf

我收到以下错误:

/bin/rm: cannot execute [Argument list too long]

该错误的解决方法是什么?mvcp命令也会发生此错误吗?如果是,该如何解决这些命令?


21
您可能会发现此链接有用
another.anon.coward



4
@jww:多年来,我一直认为bash属于“程序员常用的软件工具”之类,可以在这里提出问题!
Vicky

@Nik-在脚本中添加“ ...”并不引人注目。当问题简化为最小,完整和可验证的示例时,仅是有关如何运行命令的问题。如果我缺少明显的东西,我深表歉意。
jww

Answers:


876

发生这种情况的原因是bash实际上将星号扩展到每个匹配的文件,从而产生了很长的命令行。

尝试这个:

find . -name "*.pdf" -print0 | xargs -0 rm

警告:这是递归搜索,还将在子目录中查找(和删除)文件。-f仅在确定不希望确认时,才使用rm命令。

您可以执行以下操作使命令非递归:

find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm

另一种选择是使用find的-delete标志:

find . -name "*.pdf" -delete

7
不,xargs专门将列表分开,并在必要时发出几个命令。
2012年

7
@Dennis:-maxdepth 1必须是路径后的第一个参数。
巴顿·奇滕登

54
Find具有-delete删除所找到文件的标志,即使没有找到,也可以使用-exec执行rm而不是调用xargs(这是3个进程和一个管道,而不是一个带有-delete或2个进程-exec)。
scragar 2014年

3
@ÉdouardLopez...但这正在读取以NULL分隔的输入。而且整体来说dangerous (broken, exploitable, etc.),是相当荒谬的。无疑,使用时要小心xargs,但事实并非如此eval/evil
恢复莫妮卡

4
@scragar通过-exec调用rm,进程数将是1 +文件数,尽管与此并发的进程数可能是2(也许可以同时执行rm进程)。使用的进程数量xargs将大大减少到2 + n,其中n是少于文件数量的一些进程(例如,文件数量/ 10,尽管可能更多取决于路径的长度)。假设find直接进行删除,则-delete应该使用using 是唯一被调用的过程。
Neuromer '16

396

tl; dr

这是命令行参数大小的内核限制。请改用for循环。

问题的根源

这是一个系统问题,与之相关execveARG_MAX一直存在。有很多有关文件(见男子execve的Debian的维基)。

基本上,扩展产生的命令(及其参数)超过了ARG_MAX限制。在内核上2.6.23,限制设置为128 kB。这个常数已经增加,您可以通过执行以下操作获取其值:

getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic

解决方案:使用for循环

for按照BashFAQ / 095的建议使用循环,除了RAM /内存空间外,没有其他限制:

空运行以确定其将删除您的期望:

for f in *.pdf; do echo rm "$f"; done

并执行它:

for f in *.pdf; do rm "$f"; done

这也是一种可移植的方法,因为glob在shell之间具有强大而一致的行为(POSIX规范的一部分)。

注意:正如一些评论所指出的那样,这确实较慢,但更易于维护,因为它可以适应更复杂的场景,例如,一个人想做的不只是一项动作。

解决方案:使用 find

如果您坚持要使用,则可以使用xargsfind但实际上不要使用xargs,因为它“在读取非NUL分隔的输入时是危险的(损坏,可利用等)”

find . -maxdepth 1 -name '*.pdf' -delete 

使用-maxdepth 1 ... -delete而不是-exec rm {} +允许find自己简单地执行所需的系统调用,而无需使用外部进程,因此速度更快(感谢@chepner comment)。

参考文献


31
很好的答案,这就是应该如何回答所有SO问题的方法。谢谢!
2015年

1
+1表示for循环。我以前用过find,但是我一直在寻找操作方法,因为我一直都在忘记选项等。for似乎更容易想起恕我直言
罗伯特·邓登

3
将其用作for f in *; do rm "$f"; done魅力
Abdul qayyum

3
find -exec解决方案似乎是速度远远超过了for循环。
threeve

2
五年后,在4.15.0(4.15.0-1019-gcp准确的说),并限制仍处于2097152.有趣的是,关于Linux的git回购寻找ARG_MAX给人呈现出的结果ARG_MAX是在131702.
马特·M.

181

find有一个-delete动作:

find . -maxdepth 1 -name '*.pdf' -delete

4
这仍然会返回“参数列表过长”。至少对我而言。使用xargs,按照丹尼斯的答案,按预期工作。
塞尔吉奥2014年

7
听起来像是发现了一个错误。
ThiefMaster 2014年

3
@Sergio也有同样的问题,这是由于名称模式周围缺少引号引起的。
卢森堡2015年

哎呀,为什么找东西的工具甚至有删除开关?真的是只有我一个人,我觉得不必多说也很危险。
mathreadler '16

2
@mathreadler它解决了一个常见的用例-exec是删除一堆文件的事实。-exec rm {} +会做同样的事情,但仍然需要启动至少一个外部进程。-delete允许find自己简单地执行所需的系统调用,而无需使用外部包装器。
chepner '16

21

另一个答案是强制xargs分批处理命令。例如一次到delete文件100cd进入目录并运行此命令:

echo *.pdf | xargs -n 100 rm


4
对于删除linux中的命令(如果您是工程师并且输入了错误),这可能是一个灾难,我相信这是“最安全的,我知道发生了什么”是最好的。如果您错过输入点,这会让您的公司在一分钟内崩溃,这不是好看的东西。
人工智能

1
我们如何才能使它成为某些命令的默认扩展?有很多“标准” Linux命令可以知道它们是否一次全部需要(例如“ rm”)
user1212212

1
请注意,这仅在echo内置shell的地方起作用。如果最终使用命令echo,则仍然会遇到程序参数限制。
Toby Speight

14

或者您可以尝试:

find . -name '*.pdf' -exec rm -f {} \;

这也会从子目录中删除文件。如何预防呢?
Vicky

@NikunjChauhan添加-maxdepth选项:find . -maxdepth 1 -name '*.pdf' -exec rm -f {} \;
乔恩·林

我无法插入maxdepth选项
Vicky

根据上面@Dennis的答案(所选答案),该选项可能是仅Linux选项。
jvriesem 2014年

12

如果您要一次删除大量文件(我今天删除的目录超过485,000个),则可能会遇到此错误:

/bin/rm: Argument list too long.

问题在于,当您键入时rm -rf **会替换为每个匹配文件的列表,例如“ rm -rf file1 file2 file3 file4”,依此类推。分配给该参数列表的内存缓冲区相对较小,如果将其填满,shell将不会执行程序。

为了解决这个问题,许多人将使用find命令查找每个文件,并将它们一个接一个地传递给“ rm”命令,如下所示:

find . -type f -exec rm -v {} \;

我的问题是我需要删除500,000个文件,并且处理时间太长。

我偶然发现了一种更快的删除文件的方法-“ find”命令内置了一个“ -delete”标志!这是我最终使用的内容:

find . -type f -delete

使用这种方法,我以每秒约2000个文件的速度删除文件-更快!

您还可以在删除文件名时显示它们:

find . -type f -print -delete

…甚至显示要删除的文件数量,然后显示删除它们所需的时间:

root@devel# ls -1 | wc -l && time find . -type f -delete
100000
real    0m3.660s
user    0m0.036s
sys     0m0.552s

谢谢。我确实sudo find . -type f -delete删除了约485,000个文件,它对我有用。花了大约20秒。
奈杰尔·奥尔德顿

11

您可以尝试以下方法:

for f in *.pdf
do
  rm $f
done

编辑:ThiefMaster的评论建议我不要向年轻的Shell的Jedis公开这种危险的做法,因此我将添加一个更“安全”的版本(为了在有人使用“ -rf ..pdf”文件时保留内容)

echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
   echo "rm -i $f" >> /tmp/dummy.sh
done

运行上述命令后,只需在收藏夹中打开/tmp/dummy.sh文件即可。编辑器,并检查每一行是否包含危险的文件名,如果找到,则将其注释掉。

然后在您的工作目录中复制dummy.sh脚本并运行它。

所有这些都是出于安全原因。


5
我认为,使用名为-rf .. .pdf
ThiefMaster

是的,但是可以,但是通常在shell中使用时,命令“应该”的发出者应该看一下他在做什么:)。实际上,我更喜欢重定向到文件,然后检查每一行。
BigMike

2
这不引用“ $ f”。那就是ThiefMaster所说的。 -rf优先于-i,因此您的第二个版本更好(没有手动检查)。而且对于提示每个文件,基本上对于批量删除没有用。
彼得·科德斯

7

您可以使用bash数组:

files=(*.pdf)
for((I=0;I<${#files[@]};I+=1000)); do
    rm -f "${files[@]:I:1000}"
done

这样,它将每步分批擦除1000个文件。


2
对于大量文件,这似乎要快得多
James Tocknell


4

RM命令有哪些可以删除文件同时的限制。

一种可能性是您可以根据文件模式使用rm命令多次删除它们,例如:

rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf

您也可以通过find命令删除它们:

find . -name "*.pdf" -exec rm {} \;

3
不可以,rm对要处理的文件数量没有任何限制(但argc不能大于INT_MAX)。这是内核对整个参数数组的最大大小的限制(这就是文件名长度很大的原因)。
Toby Speight

3

如果它们是带有空格或特殊字符的文件名,请使用:

find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;

这句话搜索当前目录(-maxdepth 1)中扩展名为pdf(-name'* .pdf')的所有文件,然后删除每个文件(-exec rm“ {}”)。

表达式{}替换文件名,“ {}”将文件名设置为字符串,包括空格或特殊字符。


尽管此代码段可以解决问题,但包括有关如何以及为什么解决该问题的说明,将确实有助于提高您的帖子质量。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加解释,并指出适用的限制和假设。
Toby Speight

整个问题-exec是您不调用shell。这里的引号绝对没有用。(它们防止在您在此命令中键入的外壳中的字符串上的任何通配符扩展和令牌拆分但该字符串{}不包含任何空格或外壳通配符。)
Tripleee

2

将表单源目录复制到目标位置时遇到相同问题

源目录中有〜3个lakcs文件

我使用的CP与-r选项,它的工作对我来说

cp -r abc / def /

它将所有文件从abc复制到def,而不会给出参数列表警告太长的时间


我不知道为什么有人对此一票否定,甚至没有对此发表评论(伙计,这是政策!)。我需要删除文件夹中的所有文件(请注意,问题不是关于PDF的问题),为此,此技巧很好用,最后要做的就是重新创建在删除时删除的文件夹我用`RM -R /路径/到/文件夹”。
托马斯Tempelmann

1
之所以起作用,是因为在OP的情况下,他使用的是*,它扩展为.pdf的巨大列表,给出一个目录将使该文件在内部得到处理,因此不必处理OP的问题。我认为是出于这个原因。如果OP在目录中嵌套目录或其他文件(不是pdf),则可能对OP不可用
Alvein 2015年

2

如果您想删除30/90天以上(+)或30/90(-)天以下的文件/文件夹,则可以使用以下ex命令

例如:对于90天(不包括90天),在删除90天文件/文件夹后,这意味着91,92 .... 100天

find <path> -type f -mtime +90 -exec rm -rf {} \;

例如:对于您要删除的最新30天文件,请使用以下命令(-)

find <path> -type f -mtime -30 -exec rm -rf {} \;

如果您想将文件保存2天以上

find <path> -type f -mtime +2 -exec gzip {} \;

如果您只想查看过去一个月的文件/文件夹。例如:

find <path> -type f -mtime -30 -exec ls -lrt {} \;

如果超过30天,则仅列出文件/文件夹,例如:

find <path> -type f -mtime +30 -exec ls -lrt {} \;

find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} \;

2

我很惊讶这里没有ulimit答案。每次遇到这个问题,我都会在这里这里结束。我知道此解决方案有局限性,但ulimit -s 65536似乎常常可以帮到我。


1

我遇到了一个充满临时图像的文件夹的问题,该文件夹每天都在增长,该命令帮助我清除了该文件夹

find . -name "*.png" -mtime +50 -exec rm {} \;

与其他命令的区别是mtime参数将仅使用早于X天(在示例中为50天)的文件

通过多次使用,减少了每次执行的日期范围,我能够删除所有不必要的文件


1

我只知道解决这个问题的方法。想法是将您拥有的pdf文件列表导出到文件中。然后将该文件分成几部分。然后删除每个部分列出的pdf文件。

ls | grep .pdf > list.txt
wc -l list.txt

wc -l是计算list.txt包含多少行。当您知道有多长时,可以决定将其分成两半。使用split -l命令例如,将其分成600行。

split -l 600 list.txt

这将创建一些名为xaa,xab,xac的文件,依您拆分方式而定。现在要将这些文件中的每个列表“导入”到命令rm中,请使用以下命令:

rm $(<xaa)
rm $(<xab)
rm $(<xac)

对不起,我的英语不好。


5
如果您有一个名为的文件,pdf_format_sucks.docx它将也将被删除... ;-)在grepping pdf文件时,应使用正确且正确的正则表达式。
FooF

1
更好,但是still_pdf_format_sucks.docx会被删除。点.".pdf"正则表达式匹配任何字符。我建议"[.]pdf$"代替.pdf
FooF

1

我几次遇到这个问题。许多解决方案都会rm为每个需要删除的文件运行命令。这是非常低效的:

find . -name "*.pdf" -print0 | xargs -0 rm -rf

我最终写了一个python脚本来删除基于文件名中前4个字符的文件:

import os
filedir = '/tmp/' #The directory you wish to run rm on 
filelist = (os.listdir(filedir)) #gets listing of all files in the specified dir
newlist = [] #Makes a blank list named newlist
for i in filelist: 
    if str((i)[:4]) not in newlist: #This makes sure that the elements are unique for newlist
        newlist.append((i)[:4]) #This takes only the first 4 charcters of the folder/filename and appends it to newlist
for i in newlist:
    if 'tmp' in i:  #If statment to look for tmp in the filename/dirname
        print ('Running command rm -rf '+str(filedir)+str(i)+'* : File Count: '+str(len(os.listdir(filedir)))) #Prints the command to be run and a total file count
        os.system('rm -rf '+str(filedir)+str(i)+'*') #Actual shell command
print ('DONE')

这对我来说非常有效。我能够在大约15分钟内清除一个文件夹中的200万个临时文件。我从一点点代码中注释掉了tar,因此,几乎没有python知识的人都可以操纵此代码。


1

还有一个:

cd  /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm

printf是内置的shell,据我所知一直都是这样。现在,由于它printf不是shell命令(而是内置命令),因此它不受“argument list too long ...发生致命错误。

因此,我们可以安全地将其与shell遍历模式一起使用,例如*.[Pp][Dd][Ff],然后通过将其输出通过管道传递到remove(rm)命令xargs,以确保它在命令行中适合足够的文件名,以免失败rm命令,这是一个shell命令。

\0printf作为空分隔符至极随后被处理的文件名xargs的命令,用它(-0)作为分隔符,所以rm不会在有空格或在文件名中其他特殊字符失败。


1
尽管此代码段可以解决问题,但包括有关如何以及为什么解决该问题的说明,将确实有助于提高您的帖子质量。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加解释,并指出适用的限制和假设。
Toby Speight

特别是,如果printf不是内置的Shell,它将受到相同的限制。
Toby Speight

0

您可以创建一个临时文件夹,将要保留的所有文件和子文件夹移到该临时文件夹中,然后删除该旧文件夹,并将该临时文件夹重命名为该旧文件夹,尝试以下示例,直到您有信心将其投入使用:

mkdir testit
cd testit
mkdir big_folder tmp_folder
touch big_folder/file1.pdf
touch big_folder/file2.pdf
mv big_folder/file1,pdf tmp_folder/
rm -r big_folder
mv tmp_folder big_folder

rm -r big_folder将删除所有文件,big_folder无论有多少。您只需要非常小心,首先要拥有要保留的所有文件/文件夹,在这种情况下,file1.pdf


0

删除*.pdf目录中的全部/path/to/dir_with_pdf_files/

mkdir empty_dir        # Create temp empty dir

rsync -avh --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/

rsync如果您有数百万个文件,则使用通配符删除特定文件可能是最快的解决方案。而且它将解决您遇到的错误。


(可选步骤):DRY RUN。要检查什么将被删除而不删除。`

rsync -avhn --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/

。。。

单击rsync提示和技巧以获取更多rsync hack


0

我发现对于非常大的文件列表(> 1e6),这些答案太慢了。这是在python中使用并行处理的解决方案。我知道,我知道这不是linux ...但这里没有其他工作。

(这节省了我几个小时)

# delete files
import os as os
import glob
import multiprocessing as mp

directory = r'your/directory'
os.chdir(directory)


files_names = [i for i in glob.glob('*.{}'.format('pdf'))]

# report errors from pool

def callback_error(result):
    print('error', result)

# delete file using system command
def delete_files(file_name):
     os.system('rm -rf ' + file_name)

pool = mp.Pool(12)  
# or use pool = mp.Pool(mp.cpu_count())


if __name__ == '__main__':
    for file_name in files_names:
        print(file_name)
        pool.apply_async(delete_files,[file_name], error_callback=callback_error)

0

当应用程序创建的数百万个无用的日志文件填充了所有索引节点时,我遇到了类似的问题。我求助于“定位”,将所有文件“定位”到一个文本文件中,然后一个一个地删除它们。花了一段时间,但完成了工作!


这非常含糊,需要locate在磁盘上仍有空间时重新安装。
Tripleee

-2

比使用xargs更安全的版本,也不是递归的: ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done

在这里过滤目录是不必要的,因为'rm'不会将其删除,并且为了简单起见可以将其删除,但是为什么要运行肯定会返回错误的东西呢?


3
指出一个明显的极端情况,这一点都不安全,并且不能使用包含换行符的文件名。 解析ls是一种常见的反模式,应绝对避免使用它,并在此处添加许多其他错误。该grep | grep仅仅是不是很优雅。
三人房12:56

无论如何,这并不是一个新的奇特问题,需要复杂的解决方案。答案find很好,并且在这里和其他地方都有充分的记录。有关此主题和相关主题的更多信息,请参见例如mywiki.wooledge.org
三胞胎

-2

使用GNU并行(sudo apt install parallel)非常容易

它运行多线程命令,其中“ {}”是传递的参数

例如

ls /tmp/myfiles* | parallel 'rm {}'


我不知道,但是我想这是因为ls直接将的输出传递给其他命令是一种危险的反模式-而且通配符的扩展在执行时会产生与ls原始rm命令相同的错误,这一事实。
Toby Speight

有关此内容的上下文,请参见ParsingLs。而且parallel让一些喜欢避免复杂性的人感到不舒服-如果您在幕后看,那是相当不透明的。请参阅清单.gnu.org / archive / html / bug-parallel / 2015-05 / msg00005.html在Stephane(Unix和Linux StackExchange灰胡子之一)和Ole Tange(Parallel的作者)之间的邮件列表线程。xargs -P也可以并行化,但是它以更简单,更笨拙的方式实现了运动部件的减少,从而使其行为更容易预测和推理。
查尔斯·达菲,


-5

下面的选项对于这个问题似乎很简单。我从其他线程获得了此信息,但它对我有所帮助。

for file in /usr/op/data/Software/temp/application/openpages-storage/*; do
    cp "$file" /opt/sw/op-storage/
done

只需运行上面的一个命令,它将完成任务。

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.