UNIXのディレクトリの下に数百のPDFがあります。PDFの名前は本当に長い(約60文字)。
次のコマンドを使用してすべてのPDFを一緒に削除しようとすると:
rm -f *.pdf
次のエラーが発生します。
/bin/rm: cannot execute [Argument list too long]
このエラーの解決策は何ですか?このエラーはmv
、cp
コマンドでも発生しますか?はいの場合、これらのコマンドの解決方法は?
UNIXのディレクトリの下に数百のPDFがあります。PDFの名前は本当に長い(約60文字)。
次のコマンドを使用してすべてのPDFを一緒に削除しようとすると:
rm -f *.pdf
次のエラーが発生します。
/bin/rm: cannot execute [Argument list too long]
このエラーの解決策は何ですか?このエラーはmv
、cp
コマンドでも発生しますか?はいの場合、これらのコマンドの解決方法は?
回答:
これが発生する理由は、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
xargs
特にリストを分割し、必要に応じていくつかのコマンドを発行します。
-maxdepth 1
パスの後の最初の引数である必要があります。
-delete
見つかったファイルを削除するためのフラグがあり、それができなかったとしても-exec
、xargsを呼び出すのではなく、rmの実行に使用する方がよいと考えられます(これは、-delete
または)で2つのプロセス-exec
。
dangerous (broken, exploitable, etc.)
、かなりばかげています。間違いなく使用するときは注意する必要がありますxargs
が、それは完全ではありませんeval/evil
。
-exec
呼び出すとrm
、プロセスの数は1 +ファイルの数になりますが、これからの同時プロセスの数は2になる場合があります(findはrmプロセスを同時に実行する可能性があります)。使用xargs
するプロセスの数は2 + nに劇的に減少します。ここで、nはファイルの数より少ないプロセスの数です(たとえば、ファイルの長さ/ 10ですが、パスの長さに依存します)。findが削除を直接行うと仮定すると、using -delete
が呼び出される唯一のプロセスである必要があります。
これは、コマンドライン引数のサイズに関するカーネルの制限です。for
代わりにループを使用してください。
これはシステムの問題であり、関連してexecve
おり、ARG_MAX
不変です。これに関するドキュメントはたくさんあります(man execve、debianのwikiを参照)。
基本的に、展開により、制限を超えるコマンド(およびそのパラメーター)が生成されARG_MAX
ます。カーネル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 echo rm "$f"; done
そしてそれを実行します:
for f in *.pdf; do rm "$f"; done
また、グロブはシェル間で強力かつ一貫した動作をするため、これは移植可能なアプローチです(POSIX仕様の一部)。
注:いくつかのコメントで述べたように、それは、より複雑なシナリオを適応させることができますように、これは確かに遅いがより保守である例えば 1だけのワンアクションよりも多くやりたいところ。
find
「NULで区切られていない入力を読み取ると危険(破損、悪用可能、など)になる」ため、xargsを使用できますfind
が、実際には使用しないでください。
find . -maxdepth 1 -name '*.pdf' -delete
の-maxdepth 1 ... -delete
代わりにを-exec rm {} +
使用find
すると、外部プロセスを使用せずに、必要なシステムコール自体を簡単に実行できるため、高速になります(@chepnerコメントのおかげ)。
for
ループについて言及する場合は+1 。find
以前使っていたのですが、いつもオプションなどを忘れてしまうので、どうすればいいのか常に調べています。for
私見を思い出しやすい
for f in *; do rm "$f"; done
魅力的な作品として使用
find -exec
解決策は、はるかに速くよりであるように思わfor
ループ。
4.15.0-1019-gcp
正確には)および制限は、Linux gitのレポにARG_MAXを探し、興味深いことに2097152のままである示す結果得られ131702.であることをARG_MAXを
find
-delete
アクションがあります:
find . -maxdepth 1 -name '*.pdf' -delete
xargs
Dennisの回答に従ってを使用すると、意図したとおりに機能します。
-exec
のファイルを削除することであるという事実に対処します。-exec rm {} +
同じことを行いますが、それでも少なくとも1つの外部プロセスを開始する必要があります。外部ラッパーを使用せずに、必要なシステムコール自体を簡単に実行-delete
できfind
ます。
別の答えはxargs
、コマンドをバッチで処理することを強制することです。たとえば、一度にdelete
ファイルに、ディレクトリ100
にcd
入れて、これを実行します。
echo *.pdf | xargs -n 100 rm
echo
はシェルが組み込まれている場所でのみ機能することに注意してください。コマンドを使用してしまった場合echo
でも、プログラムの引数の制限に遭遇します。
または、あなたは試すことができます:
find . -name '*.pdf' -exec rm -f {} \;
find . -maxdepth 1 -name '*.pdf' -exec rm -f {} \;
非常に多数のファイルを一度に削除しようとしている場合(私は今日485,000以上のディレクトリを削除しました)、おそらくこのエラーに遭遇します:
/bin/rm: Argument list too long.
問題は、のようなものを入力するとrm -rf *
、*
「rm -rf file1 file2 file3 file4」のように、一致するすべてのファイルのリストに置き換えられることです。この引数のリストを格納するために割り当てられたメモリの比較的小さなバッファがあり、それがいっぱいになると、シェルはプログラムを実行しません。
この問題を回避するには、多くの人がfindコマンドを使用してすべてのファイルを検索し、次のように1つずつ「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千のファイルを削除しましたが、うまくいきました。約20秒かかりました。
あなたはこれを試すことができます:
for f in *.pdf
do
rm $f
done
編集:ThiefMasterのコメントは、若いシェルのjedisにそのような危険な慣行を開示しないように私に提案しているので、より「安全な」バージョンを追加します(誰かが「-rf。..pdf」ファイルを持っているときに物事を保存するため)
echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
echo "rm -i $f" >> /tmp/dummy.sh
done
上記を実行した後、お気に入りの/tmp/dummy.shファイルを開きます。エディターで危険なファイル名がないかすべての行をチェックし、見つかった場合はコメントアウトします
次に、ダミーの.shスクリプトを作業ディレクトリにコピーして実行します。
これはすべてセキュリティ上の理由からです。
-rf .. .pdf
-rf
はに優先する-i
ため、2番目のバージョンは(手動検査なしで)優れていません。また、すべてのファイルを要求するプロンプトが表示されるため、基本的に一括削除には役に立ちません。
あなたはbash配列を使うことができます:
files=(*.pdf)
for((I=0;I<${#files[@]};I+=1000)); do
rm -f "${files[@]:I:1000}"
done
この方法では、ステップごとに1000ファイルのバッチで消去されます。
rmコマンドを使用すると、同時に削除できるファイルの制限があります。
rmコマンドを複数回使用してそれらを削除できる1つの可能性は、次のようなファイルパターンに基づいています
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
また、findコマンドを使用して削除することもできます。
find . -name "*.pdf" -exec rm {} \;
rm
処理するファイル数にそのような制限はありません(ただし、を超えることはargc
できませんINT_MAX
)。これは、引数配列全体の最大サイズに対するカーネルの制限です(そのため、ファイル名の長さが重要になります)。
それらがスペースまたは特殊文字を含むファイル名である場合は、以下を使用します。
find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;
この文は、現在のディレクトリ(-maxdepth 1)にある拡張子pdf(-name '* .pdf')のすべてのファイルを検索し、各ファイルを削除します(-exec rm "{}")。
式{}はファイル名を置き換え、 "{}"はスペースまたは特殊文字を含む文字列としてファイル名を設定します。
-exec
は、シェルを呼び出さないことです。ここでの引用は、何の役にも立たない。(これらは、このコマンドを入力するシェル内の文字列でのワイルドカード拡張とトークン分割を防止しますが、文字列に{}
は空白またはシェルワイルドカード文字が含まれていません。)
フォームのソースディレクトリを宛先にコピーしているときに同じ問題に直面していました
ソースディレクトリにファイルが3つまでありました
私はオプション-rでcpを使用し、それは私のために働いた
cp -r abc / def /
引数リストが長すぎるという警告を出さずに、abcからdefにすべてのファイルをコピーします
これも試してください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日以上ギズしたい場合
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 {} \;
私はこれを回避する方法を知っているだけです。アイデアは、あなたが持っているPDFファイルのリストをファイルにエクスポートすることです。次に、そのファイルをいくつかの部分に分割します。次に、各部分にリストされているPDFファイルを削除します。
ls | grep .pdf > list.txt
wc -l list.txt
wc -lは、list.txtに含まれる行数をカウントします。それがどのくらいの長さであるかがわかったら、それを半分、4つ、または何かに分割することを決定できます。split -lコマンドの使用たとえば、それぞれを600行に分割します。
split -l 600 list.txt
これにより、xaa、xab、xacという名前のファイルがいくつか作成されます。分割方法によって異なります。これらのファイルの各リストをコマンドrmに「インポート」するには、次のコマンドを使用します。
rm $(<xaa)
rm $(<xab)
rm $(<xac)
私の悪い英語でごめんなさい。
pdf_format_sucks.docx
これも削除されます... ;-) PDFファイルをgrepする場合は、適切で正確な正規表現を使用する必要があります。
still_pdf_format_sucks.docx
削除されます。正規表現のドット.
は、".pdf"
任意の文字と一致します。の"[.]pdf$"
代わりに私はお勧めします.pdf
。
私はこの問題に数回遭遇しました。ソリューションの多くは、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')
これは私にとって非常にうまくいきました。フォルダー内の200万を超える一時ファイルを約15分でクリアすることができました。ほんの少しのコードからtarにコメントを付けたので、Pythonの知識が最小限またはまったくない人でもこのコードを操作できます。
そしてもう一つ:
cd /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm
printf
シェル組み込みであり、私が知る限り、常にそうでした。これprintf
がシェルコマンドではなく(組み込み)であることを考えると、「argument list too long ...
」致命的なエラーの影響を受けません。
したがって*.[Pp][Dd][Ff]
、のようなシェルグロビングパターンで安全に使用できます。次に、その出力をパイプラインで削除(rm
)コマンドに渡しますxargs
。これにより、コマンドラインで十分なファイル名に適合し、コマンドが失敗しないようにしますrm
。これはシェルです。コマンド。
\0
でprintf
それまでに処理されWICHファイル名のヌルセパレータとして機能するxargs
(それを使用して、コマンド-0
セパレータとして)ので、rm
空白またはファイル名に他の特殊文字がある場合に失敗しません。
printf
が組み込みシェルでない場合は、同じ制限が適用されます。
一時フォルダーを作成し、保持するすべてのファイルとサブフォルダーを一時フォルダーに移動してから、古いフォルダーを削除し、一時フォルダーの名前を古いフォルダーに変更して、信頼できる状態になるまでこの例を試してください。
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
*.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のヒントとコツをクリックしてください
非常に大きなファイルのリスト(> 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)
xargsを使用するよりも少し安全なバージョンで、再帰的でもありません。
ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done
'rm'はディレクトリを削除しないので、ここでディレクトリをフィルタリングすることは少し不要ですが、簡単にするために削除できますが、なぜエラーを返すものを実行するのでしょうか。
ls
は一般的には避けなければならない一般的なアンチパターンであり、多くのバグがここに追加されます。grep | grep
ただ非常にエレガントではありません。
find
は良く、ここや他の場所で十分に文書化されています。このトピックおよび関連トピックの詳細については、たとえばmywiki.wooledge.orgを参照してください。
GNU並列(sudo apt install parallel
)の使用は非常に簡単です
マルチスレッドのコマンドを実行します。ここで、「{}」は渡された引数です
例えば
ls /tmp/myfiles* | parallel 'rm {}'
ls
を他のコマンドに直接渡すのは危険なアンチパターンであるためだと思います。つまり、ワイルドカードを展開すると、実行時ls
に元のrm
コマンドで経験したのと同じエラーが発生するという事実です。。
parallel
、複雑さを回避することを好む一部の人々を不快にします-内面を見ると、それはかなり不透明です。Stepsane(UnixおよびLinuxのStackExchange greybeardsの1つ)とOle Tange(Parallelの作者)の間の、lists.gnu.org / archive / html / bug -parallel / 2015-05 / msg00005.htmlにあるメーリングリストのスレッドを参照してください。xargs -P
並列化も行いますが、可動部分が少なく、より単純で無駄のない方法で行われるため、動作の予測と推論がはるかに容易になります。
最初の100ファイルを削除するには:
rm -rf 'ls | 頭-100 '