web-dev-qa-db-ja.com

Rm、cp、mvコマンドの引数リストが長すぎるエラー

UNIXのディレクトリの下に数百のPDFがあります。 PDFの名前は本当に長いです(約60文字)。

次のコマンドを使用してすべてのPDFをまとめて削除しようとしました。

rm -f *.pdf

次のようなエラーが表示されます。

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

このエラーの解決策は何ですか?このエラーはmvおよびcpコマンドでも発生しますか?もしそうなら、これらのコマンドを解決する方法?

512
Vicky

これが起こる理由は、bashが実際に一致するすべてのファイルにアスタリスクを拡張し、非常に長いコマンドラインを生成するためです。

これを試して:

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

警告: これは再帰的な検索であり、サブディレクトリにあるファイルも見つけます(そして削除します)。確認したくない場合にのみ、rmコマンドに-fを付けてください。

コマンドを非再帰的にするには、次のようにします。

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

他の選択肢はfindの-deleteフラグを使うことです:

find . -name "*.pdf" -delete
716
DPlusV

tl; dr

これは、コマンドライン引数のサイズに関するカーネルの制限です。代わりにforループを使用してください。

問題の原因

これは、execveおよびARG_MAX定数に関連するシステムの問題です。それについては多くのドキュメントがあります( man execvedebian's wiki を参照)。

基本的に、展開はARG_MAX制限を超えるcommand(およびパラメーター付き)を生成します。カーネル2.6.23では、制限は128 kBに設定されていました。この定数は増加しており、次を実行することで値を取得できます。

getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic

解決策:forループを使用する

BashFAQ/095 で推奨されているforループを使用します。RAM/メモリスペース以外には制限はありません。

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

また、globはシェル間で強力で一貫した動作を行うため、移植可能なアプローチです( POSIX仕様の一部 )。

注:いくつかのコメントで指摘されているように、これは実際にはより遅くなりますが、より複雑なシナリオに適応できるため、よりメンテナンスしやすくなりますeg1つ以上のアクションを実行したい場合。

解決策:findを使用する

あなたが主張する場合、findを使用できますが、実際にはxargsを使用しないでください」は危険です( NULで区切られていない入力を読み取るとき」

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

-maxdepth 1 ... -deleteの代わりに-exec rm {} +を使用すると、findが外部プロセスを使用せずに必要なシステムコール自体を実行できるため、高速になります( @ chepner comment のおかげです)。

参照資料

339
Édouard Lopez

findには-deleteアクションがあります。

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

別の答えは、xargsにコマンドをまとめて処理させることです。たとえば、ファイル100を一度にdeleteにするには、ディレクトリにcdを入力して次のコマンドを実行します。

echo *.pdf | xargs -n 100 rm

19

またはあなたが試すことができます:

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

非常に多数のファイルを一度に削除しようとしている場合(私は今日485,000以上のディレクトリを削除しました)、おそらくこのエラーに遭遇するでしょう:

/bin/rm: Argument list too long.

問題は、rm -rf *のように入力すると、*が、「rm -rfファイル1ファイル2ファイル3ファイル4」のように、一致するすべてのファイルのリストに置き換えられることです。この引数のリストを格納するために割り当てられたメモリの比較的小さいバッファがあり、それがいっぱいになると、シェルはプログラムを実行しません。

この問題を回避するために、多くの人がfindコマンドを使用してすべてのファイルを検索し、それらを1つずつ次のように“ rm”コマンドに渡します。

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

私の問題は、私が50万のファイルを削除する必要があり、それは時間がかかりすぎていたということです。

もっと早くファイルを削除する方法を見つけました。「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
9
Bibin Joseph

あなたはこれを試すことができます:

for f in *.pdf
do
  rm $f
done

編集:ThiefMasterのコメントは、このような危険なやり方を若いシェルのジェダイには開示しないように私に示唆しているので、私はもっと「より安全な」バージョンを追加する。

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

上記を実行した後、あなたの好みで/tmp/dummy.shファイルを開くだけです。危険なファイル名については一行ごとにチェックし、見つかった場合はコメントアウトしてください。

次に、作業ディレクトリにdummy.shスクリプトをコピーして実行します。

これはセキュリティ上の理由からです。

9
BigMike

Bash配列を使うことができます:

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

このようにして、ステップごとに1000ファイルのバッチで消去されます。

5
danjperron

あなたはこのコマンドを使うことができます

find -name "*.pdf"  -delete
4
Sarath Ak

rm コマンドには、同時に削除できるファイルの制限があります。

ファイルパターンに基づいて rm コマンドベースを複数回使用して削除できる可能性があります。

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

find commandから削除することもできます。

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

スペースや特殊文字を含むファイル名の場合は、次のようにします。

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

この文は、現在のディレクトリ(-maxdepth 1)にあるすべてのファイルを拡張子pdf(-name '* .pdf')で検索し、それぞれ削除します(-exec rm "{}")。

式{}はファイルの名前を置き換え、 "{}"はファイル名をスペースや特殊文字を含む文字列として設定します。

find . -type f -name '*xxx' -print -delete

2
pigletfly

ここにulimitの答えがないのは驚きです。私がこの問題を抱えるたびに、私は結局 ここ / ここ になります。私はこの解決策には限界があることを理解していますが、ulimit -s 65536は私にとってしばしばトリックをするようです。

2
dps

フォームのソースディレクトリをコピー先にコピーしているときに同じ問題に直面していました

ソースディレクトリにファイルがありました

私は cpと-r - オプションを使用

cp -r abc/def/

長すぎる引数リストを警告せずに、すべてのファイルをabcからdefにコピーします。

2
user3405020

そしてもう一つ:

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

printfはShellビルトインであり、私が知る限り、常にそうでした。 printfがシェルコマンドではない(ただしビルトイン)場合、「argument list too long ...」致命的エラーの影響を受けません。

したがって、*.[Pp][Dd][Ff]などのシェルグロビングパターンで安全に使用でき、その出力を削除して(rm)コマンドをxargsに渡すことで、十分なファイル名に収まるようにします。シェルコマンドであるrmコマンドに失敗しないようにコマンドライン。

printf\0は、xargsコマンドによって処理されるファイル名のヌル区切り文字として機能し、それを区切り文字(-0)として使用するため、rmは、ファイル名に空白またはその他の特殊文字が含まれていても失敗しません。

1
lind

私はこの問題に何度か遭遇した。解決策の多くは、削除する必要がある個々のファイルごとにrmコマンドを実行します。これは非常に非効率的です。

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

ファイル名の最初の4文字に基づいてファイルを削除するためのPythonスクリプトを書きました。

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
Pedro Montero

一時フォルダを作成し、保持したいすべてのファイルとサブフォルダを一時フォルダに移動してから、古いフォルダを削除し、一時フォルダの名前を古いフォルダに変更することができます。

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
Keithhn

30/90日を超えて(+)または30/90( - )日を超えて削除したい場合は、以下のexコマンドを使用できます。

例: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日以上のファイルをgizしたい場合

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

あなたが過去1ヶ月だけからファイル/フォルダーを見たいと思うならば。例:

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 {} \;
0
raja

すべてのiノードを埋め尽くすアプリケーションによって作成された無駄なログファイルが何百万もあるとき、私は同様の問題に直面しました。私は "locate"に頼って、すべてのファイルをテキストファイルにまとめ、それから一つずつ削除しました。しばらく時間がかかりましたが、仕事をしました!

0
asatsi

私はこれを回避する方法を知っているだけです。アイデアはあなたが持っている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)

私の悪い英語ですみません。

0
user219776

私は、ファイルのリストが非常に大きい(> 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 __== '__main__':
    for file_name in files_names:
        print(file_name)
        pool.apply_async(delete_files,[file_name], error_callback=callback_error)
0
mmann1123

ディレクトリ内のすべての*.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のヒントとコツ

0
Raman Kathpalia

大量のファイルを削除している間にサーバーやシステムを レスポンシブ に維持する必要がある場合は、各deleteステートメント間のsleepが良い方法になります。

find . -name "*.pdf" -print0 | while read -d $'\0' file
do
    rm "$file"
    sleep 0.005 # Sleeps for 5ms, Tweak as needed
done
0
Ecker00