รายการอาร์กิวเมนต์มีข้อผิดพลาดนานเกินไปสำหรับคำสั่ง rm, cp, mv


629

ฉันมี PDF หลายร้อยไฟล์ภายใต้ไดเรกทอรีใน UNIX ชื่อของ PDF นั้นยาวมาก (ประมาณ 60 ตัวอักษร)

เมื่อฉันพยายามลบ PDF ทั้งหมดด้วยกันโดยใช้คำสั่งต่อไปนี้:

rm -f *.pdf

ฉันได้รับข้อผิดพลาดต่อไปนี้:

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

การแก้ไขข้อผิดพลาดนี้คืออะไร? ข้อผิดพลาดนี้เกิดขึ้นสำหรับmvและcpคำสั่งด้วยหรือไม่ ถ้าใช่จะแก้ไขคำสั่งเหล่านี้ได้อย่างไร?


21
คุณอาจพบว่าลิงค์นี้มีประโยชน์
another.anon.coward


1
นอกจากนี้ยังสามารถใช้งานได้ที่http://mywiki.wooledge.org/BashFAQ/095
Lorenzo Belli

4
@ jww: และฉันยังคงคิดมานานหลายปีว่าทุบตีอยู่ภายใต้ "เครื่องมือซอฟต์แวร์ที่ใช้กันทั่วไปโดยโปรแกรมเมอร์" - หมวดหมู่ที่มีคำถามที่สามารถถามได้ที่นี่!
Vicky

@Nik - การเพิ่ม "... ในสคริปต์" ไม่น่าสนใจ เมื่อปัญหาลดลงเป็นตัวอย่างที่น้อยที่สุดสมบูรณ์และตรวจสอบได้เป็นเพียงคำถามเกี่ยวกับวิธีเรียกใช้คำสั่ง ฉันขอโทษถ้าฉันขาดอะไรบางอย่างที่ชัดเจน
jww

คำตอบ:


876

เหตุผลนี้เกิดขึ้นเนื่องจากทุบตีขยายเครื่องหมายดอกจันทุก ๆ ไฟล์ที่ตรงกันสร้างบรรทัดคำสั่งที่ยาวมาก

ลองสิ่งนี้:

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

คำเตือน:นี่เป็นการค้นหาแบบเรียกซ้ำและจะค้นหา (และลบ) ไฟล์ในไดเรกทอรีย่อยด้วย แทคใน-fคำสั่ง RM เท่านั้นถ้าคุณแน่ใจว่าคุณไม่ต้องการยืนยัน

คุณสามารถทำสิ่งต่อไปนี้เพื่อทำให้คำสั่งไม่เรียกซ้ำ:

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

ตัวเลือกอื่นคือใช้-deleteค่าสถานะของ find :

find . -name "*.pdf" -delete

7
ไม่xargsแยกรายการโดยเฉพาะและออกคำสั่งหลายคำสั่งหากจำเป็น
tripleee

7
@Dennis: -maxdepth 1จำเป็นต้องเป็นอาร์กิวเมนต์แรกหลังจากเส้นทาง
Barton Chittenden

54
Find มีการ-deleteตั้งค่าสถานะเพื่อลบไฟล์ที่พบและแม้ว่ามันจะไม่ถือว่าเป็นวิธีที่ดีกว่าที่จะใช้-execในการรัน rm แทนที่จะเรียกใช้ xargs (ซึ่งตอนนี้เป็น 3 กระบวนการและไพพ์แทนที่จะเป็นกระบวนการเดียวด้วย-deleteหรือ 2 กระบวนการด้วย-exec)
scragar

3
@ ÉdouardLopez ... แต่นี่คือการอ่านอินพุตที่คั่นด้วย NULL และทั้งหมดdangerous (broken, exploitable, etc.)นั้นไร้สาระอย่างเป็นธรรม ไม่ต้องสงสัยคุณควรจะระมัดระวังในการใช้แต่ก็ไม่มากxargs eval/evil
Reinstate Monica โปรด

4
@scragar เมื่อมีการ-execเรียกrmจำนวนโปรเซสจะเป็น 1 + จำนวนไฟล์แม้ว่าจำนวนของโปรเซสพร้อมกันจากนี้อาจเป็น 2 (อาจจะพบว่ารันเอ็มเอ็มโปรเซสพร้อมกัน) จำนวนกระบวนการที่ใช้xargsจะลดลงอย่างมากเป็น 2 + n โดยที่ n เป็นกระบวนการจำนวนน้อยกว่าจำนวนไฟล์ (พูดจำนวนไฟล์ / 10 แม้ว่าจะมีความเป็นไปได้มากขึ้นขึ้นอยู่กับความยาวของเส้นทาง) สมมติว่าการค้นหาทำการลบโดยตรงการใช้-deleteควรเป็นกระบวนการเดียวที่จะถูกเรียกใช้
Neuralmer

396

TL; DR

มันเป็นข้อ จำกัด เคอร์เนลกับขนาดของอาร์กิวเมนต์บรรทัดคำสั่ง ใช้การforวนซ้ำแทน

ต้นกำเนิดของปัญหา

นี่เป็นปัญหาของระบบที่เกี่ยวข้องกับexecveและARG_MAXคงที่ มีเอกสารมากมายเกี่ยวกับเรื่องนั้น (ดูman execve , Wiki ของเดเบียน )

โดยทั่วไปการขยายสร้างคำสั่ง (พร้อมพารามิเตอร์) ที่เกินARG_MAXขีด จำกัด บนเคอร์เนล2.6.23ขีด จำกัด 128 kBตั้งอยู่ที่ ค่าคงที่นี้เพิ่มขึ้นและคุณสามารถรับค่าได้โดยดำเนินการ:

getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic

วิธีแก้ปัญหา: การใช้forLoop

ใช้การforวนซ้ำตามที่แนะนำในBashFAQ / 095และไม่มีข้อ จำกัด ยกเว้นพื้นที่ RAM / หน่วยความจำ:

วิ่งให้แห้งเพื่อยืนยันว่าจะลบสิ่งที่คุณคาดหวัง

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

และดำเนินการ:

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

นอกจากนี้ยังเป็นวิธีการพกพาเนื่องจาก glob มีพฤติกรรมที่แข็งแกร่งและสอดคล้องกันระหว่างเชลล์ ( ส่วนหนึ่งของข้อมูลจำเพาะ POSIX )

หมายเหตุ: ดังที่บันทึกไว้โดยความคิดเห็นหลายประการนี่เป็นเรื่องที่ช้ากว่า แต่สามารถบำรุงรักษาได้มากกว่าเนื่องจากสามารถปรับเปลี่ยนสถานการณ์ที่ซับซ้อนได้มากขึ้นเช่นในกรณีที่ต้องการทำมากกว่าหนึ่งการกระทำ

โซลูชัน: การใช้ find

หากคุณยืนยันคุณสามารถใช้findแต่ไม่ใช้ xargsเพราะ"เป็นอันตราย (แตกหักใช้ประโยชน์ได้ ฯลฯ ) เมื่ออ่านอินพุตที่ไม่มีการคั่นด้วย NUL" :

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

การใช้-maxdepth 1 ... -deleteแทนที่จะ-exec rm {} +อนุญาตให้findเรียกใช้งานระบบที่จำเป็นต้องเรียกตัวเองโดยไม่ต้องใช้กระบวนการภายนอกจึงเร็วขึ้น (ขอบคุณ@chepner ความคิดเห็น )

อ้างอิง


31
คำตอบที่ดีนี่คือวิธีการตอบคำถาม SO ทั้งหมด ขอบคุณ!
tommed

1
+1 สำหรับการพูดถึงforลูป ฉันเคยใช้มาfindก่อน แต่ฉันมักจะมองหาวิธีที่จะทำมันในขณะที่ฉันลืมตัวเลือก ฯลฯ ตลอดเวลา forดูเหมือนจะง่ายกว่าที่จะจำ IMHO
Robert Dundon

3
ใช้มันเพื่อfor f in *; do rm "$f"; doneใช้เป็นเสน่ห์
อับดุล qayyum

3
find -execทางออกที่น่าจะเป็นเร็วกว่าforห่วง
threeve

2
ห้าปีต่อมาที่ 4.15.0 ( 4.15.0-1019-gcpแน่นอน) และขีด จำกัด ยังคงอยู่ที่ 2097152 ที่น่าสนใจพอค้นหา ARG_MAX บน linux git repo ให้ผลแสดงARG_MAX เป็น 131702
Matt M.

181

findมีการ-deleteกระทำ:

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

4
สิ่งนี้จะยังคงกลับมา "รายการอาร์กิวเมนต์ยาวเกินไป" อย่างน้อยก็สำหรับฉันมันทำ การใช้xargsงานตามคำตอบของเดนนิส
Sergio

7
นั่นฟังดูเหมือนจุดบกพร่องในการค้นหา
ThiefMaster

3
@Sergio มีปัญหาเดียวกันมันเกิดจากคำพูดที่หายไปรอบ ๆ รูปแบบชื่อ
Luxian

ทำไมเครื่องมือสำหรับค้นหาสิ่งของถึงมีสวิตช์สำหรับลบ? เป็นเพียงฉันจริง ๆ ที่พบว่าไม่จำเป็นต้องพูดน้อยและอันตราย
คณิตศาสตร์ที่

2
@ Mathreadler มันเป็นความจริงที่ว่ากรณีการใช้งานทั่วไปสำหรับ-execคือการลบกลุ่มของไฟล์ -exec rm {} +จะทำสิ่งเดียวกัน แต่ยังต้องเริ่มต้นกระบวนการภายนอกอย่างน้อยหนึ่งกระบวนการ -deleteอนุญาตให้findเรียกใช้งานระบบที่ต้องการได้อย่างง่ายดายโดยไม่ต้องใช้ wrapper ภายนอก
chepner

21

คำตอบก็คือบังคับxargsให้ประมวลผลคำสั่งเป็นชุด ตัวอย่างเช่นdeleteไฟล์100ในแต่ละครั้งcdเข้าสู่ไดเรกทอรีและเรียกใช้สิ่งนี้:

echo *.pdf | xargs -n 100 rm


4
สำหรับการลบคำสั่งใน linux ซึ่งอาจเป็นหายนะหากคุณเป็นวิศวกรและคุณพิมพ์ผิดฉันเชื่อว่ามันเป็น 'ที่ปลอดภัยที่สุดและฉันรู้ว่าเกิดอะไรขึ้น' เป็นสิ่งที่ดีที่สุด ไม่ใช่สิ่งแฟนซีหากคุณพลาดการพิมพ์จุดจะทำให้ บริษัท ของคุณพังในหนึ่งนาที
ปัญญาประดิษฐ์

1
เราจะทำให้นี่เป็นส่วนขยายเริ่มต้นสำหรับคำสั่งบางอย่างได้อย่างไร มีคำสั่ง linux "มาตรฐาน" ที่ดีมากมายซึ่งเป็นที่ทราบกันว่าพวกเขาต้องการทั้งหมดในครั้งเดียวหรือไม่ (เช่น "rm")
user1212212

1
โปรดทราบว่านี่จะทำงานเฉพาะที่ที่echoมีเชลล์อยู่ภายใน หากคุณลงเอยด้วยการใช้คำสั่งechoคุณจะยังคงพบข้อ จำกัด การขัดแย้งของโปรแกรม
Toby Speight

14

หรือคุณสามารถลอง:

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

ไฟล์นี้จะลบจากไดเรกทอรีย่อยเช่นกัน จะป้องกันได้อย่างไร
Vicky

@NikunjChauhan เพิ่มตัวเลือก -maxdepth:find . -maxdepth 1 -name '*.pdf' -exec rm -f {} \;
Jon Lin

ฉันไม่สามารถแทรกตัวเลือก maxdepth ได้
Vicky

ตัวเลือกนั้นอาจเป็นตัวเลือกสำหรับ Linux เท่านั้นตามคำตอบของ @ Dennis ด้านบน (คำตอบที่เลือก)
jvriesem

12

หากคุณพยายามลบไฟล์จำนวนมากในคราวเดียว (ฉันลบไดเรกทอรีที่มี 485,000+ วันนี้) คุณอาจพบข้อผิดพลาดนี้:

/bin/rm: Argument list too long.

ปัญหาคือว่าเมื่อคุณพิมพ์สิ่งที่ต้องการrm -rf *ที่*จะถูกแทนที่ด้วยรายชื่อของไฟล์ที่ตรงกันทุกที่เช่น“RM -rf file1 file2 file3 file4” และอื่น ๆ มีบัฟเฟอร์หน่วยความจำขนาดเล็กที่จัดสรรให้กับการจัดเก็บรายการอาร์กิวเมนต์นี้และถ้าเต็มแล้วเชลล์จะไม่เรียกใช้งานโปรแกรม

เพื่อแก้ไขปัญหานี้ผู้คนจำนวนมากจะใช้คำสั่ง 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 วินาที
Nigel Alderton

11

คุณสามารถลองสิ่งนี้:

for f in *.pdf
do
  rm $f
done

แก้ไข: ความคิดเห็น ThiefMaster แนะนำให้ฉันไม่เปิดเผยการปฏิบัติที่เป็นอันตรายเช่นนี้ต่อเจไดของหอยเชลล์ดังนั้นฉันจะเพิ่มเวอร์ชัน "ปลอดภัย" มากขึ้น (เพื่อประโยชน์ในการรักษาสิ่งต่าง ๆ เมื่อมีคนมีไฟล์ "-rf. ..pdf")

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

หลังจากเรียกใช้ข้างต้นเพียงแค่เปิดไฟล์ /tmp/dummy.sh ใน fav ของคุณ แก้ไขและตรวจสอบชื่อไฟล์ที่เป็นอันตรายทุกบรรทัดเพื่อแสดงความคิดเห็นหากพบ

จากนั้นคัดลอกสคริปต์ dummy.sh ใน dir ที่ทำงานและเรียกใช้

ทั้งหมดนี้เพื่อเหตุผลด้านความปลอดภัย


5
ฉันคิดว่ามันจะทำสิ่งที่ดีจริงๆด้วยไฟล์ชื่อเช่น-rf .. .pdf
ThiefMaster

ใช่มันจะ แต่โดยทั่วไปเมื่อใช้ในเชลล์ผู้ออกคำสั่ง "ควร" ให้ดูสิ่งที่เขาทำ :) ที่จริงฉันชอบที่จะเปลี่ยนเส้นทางไปยังไฟล์แล้วตรวจสอบทุกแถว
BigMike

2
สิ่งนี้ไม่ได้อ้างอิง "$ f" นั่นคือสิ่งที่ ThiefMaster พูดถึง -rfมีความสำคัญมากกว่า-iรุ่นที่ 2 ของคุณจึงไม่ดีกว่า (หากไม่มีการตรวจสอบด้วยตนเอง) และโดยทั่วไปจะไม่มีประโยชน์สำหรับการลบจำนวนมากเนื่องจากการพร้อมท์สำหรับทุกไฟล์
Peter Cordes

7

คุณสามารถใช้ bash array:

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

วิธีนี้มันจะลบไฟล์เป็นชุดละ 1,000 ไฟล์ต่อขั้นตอน


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คือคุณไม่ได้เรียกเชลล์ คำพูดที่นี่ไม่มีประโยชน์อะไรเลย (พวกเขาป้องกันไม่ให้ขยายตัวสัญลักษณ์แทนใด ๆ และแยกโทเค็นสตริงในเปลือกที่คุณพิมพ์ในคำสั่งนี้แต่สาย{}ไม่ได้มีช่องว่างใด ๆ หรือเปลือกอักขระตัวแทน.)
tripleee

2

ฉันประสบปัญหาเดียวกันในขณะที่คัดลอกไดเรกทอรีแหล่งที่มาของรูปแบบไปยังปลายทาง

ไดเรกทอรีต้นทางมีไฟล์ ~ 3 lakcs

ฉันใช้cp กับตัวเลือก -rและมันใช้ได้กับฉัน

cp -r abc / def /

มันจะคัดลอกไฟล์ทั้งหมดจาก abc ไปยัง def โดยไม่แจ้งเตือนรายการอาร์กิวเมนต์นานเกินไป


ฉันไม่รู้ว่าทำไมมีคนลงคะแนนในเรื่องนี้โดยไม่ได้แสดงความคิดเห็นเกี่ยวกับเรื่องนั้น (นั่นเป็นนโยบายเลย!) ฉันต้องการลบไฟล์ทั้งหมดในโฟลเดอร์ (คำถามนี้ไม่ได้เฉพาะเกี่ยวกับ PDF, จิตใจคุณ) และสำหรับที่เคล็ดลับนี้ทำงานได้ดีทุกคนต้องทำในท้ายที่สุดคือการสร้างโฟลเดอร์ที่ถูกลบพร้อมเมื่อ ฉันใช้ `rm -R / path / to / folder"
Thomas Tempelmann

1
มันใช้งานได้เพราะในกรณีของ OP เขาใช้ * ซึ่งขยายไปยังรายการขนาดใหญ่ของ. pdf การให้ไดเรกทอรีจะทำให้สิ่งนี้ได้รับการปฏิบัติภายในดังนั้นไม่ต้องจัดการกับปัญหาของ OP ฉันคิดว่ามันลดลงเพราะเหตุผลนั้น อาจไม่สามารถใช้กับ OP ได้หากเขามีไดเรกทอรีที่ซ้อนกันหรือไฟล์อื่น ๆ (ไม่ใช่ pdf) ในไดเรกทอรีของเขา
Alvein

2

ลองทำเช่นนี้หากคุณต้องการลบมากกว่า 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 {} \;

หากคุณต้องการ giz ไฟล์มากกว่า 2 วันไฟล์

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

หากคุณต้องการดูไฟล์ / โฟลเดอร์เฉพาะจากที่ผ่านมาหนึ่งเดือน Ex:

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

มากกว่า 30 วันขึ้นไปเท่านั้นจากนั้นแสดงรายการไฟล์ / โฟลเดอร์ Ex:

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')

มันใช้งานได้ดีมากสำหรับฉัน ฉันสามารถลบไฟล์ชั่วคราวมากกว่า 2 ล้านไฟล์ในโฟลเดอร์ในเวลาประมาณ 15 นาที ฉันแสดงความคิดเห็น tar จากโค้ดเล็กน้อยเพื่อให้ทุกคนที่มีความรู้น้อยถึงไม่มีความรู้เกี่ยวกับงูหลามสามารถจัดการโค้ดนี้ได้


1

และอีกหนึ่ง:

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ทำหน้าที่เป็นตัวคั่น null สำหรับชื่อไฟล์ชมีการประมวลผลแล้วโดยxargsคำสั่งที่ใช้มัน ( -0) เป็นตัวคั่นจึงrmไม่ล้มเหลวเมื่อมีช่องว่างสีขาวหรืออักขระพิเศษในชื่อไฟล์


1
ในขณะที่ข้อมูลโค้ดนี้อาจแก้ปัญหาได้รวมถึงคำอธิบายเกี่ยวกับวิธีการและสาเหตุที่การแก้ปัญหาจะช่วยปรับปรุงคุณภาพของโพสต์ของคุณได้อย่างไร โปรดจำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตไม่ใช่เพียงแค่คนที่ถามตอนนี้! โปรดแก้ไขคำตอบของคุณเพื่อเพิ่มคำอธิบายและระบุข้อ จำกัด และสมมติฐานที่ใช้
Toby Speight

โดยเฉพาะอย่างยิ่งหากprintfไม่ใช่เชลล์ในตัวมันจะมีข้อ จำกัด เหมือนกัน
Toby Speight

0

คุณสามารถสร้างโฟลเดอร์ temp ย้ายไฟล์และโฟลเดอร์ย่อยทั้งหมดที่คุณต้องการเก็บไว้ในโฟลเดอร์ temp จากนั้นลบโฟลเดอร์เก่าและเปลี่ยนชื่อโฟลเดอร์ temp เป็นโฟลเดอร์เก่าลองตัวอย่างนี้จนกว่าคุณจะมั่นใจได้ว่าจะใช้งานได้จริง:

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 เพื่ออ่านคำแนะนำเพิ่มเติม


0

ฉันพบว่าสำหรับรายการไฟล์ที่มีขนาดใหญ่มาก (> 1e6) คำตอบเหล่านี้ช้าเกินไป นี่คือวิธีการแก้ปัญหาโดยใช้การประมวลผลแบบขนานในหลาม ฉันรู้ฉันรู้ว่านี่ไม่ใช่ 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

ฉันประสบปัญหาคล้ายกันเมื่อมีไฟล์บันทึกที่ไร้ประโยชน์นับล้านที่สร้างโดยแอปพลิเคชันซึ่งเติมเต็ม inodes ทั้งหมด ฉันหันไปหา "ค้นหา" รับไฟล์ทั้งหมด "ตั้งอยู่" d ลงในไฟล์ข้อความแล้วลบออกทีละไฟล์ ใช้เวลาสักครู่ แต่ทำงานได้!


นี่ค่อนข้างคลุมเครือและคุณต้องติดตั้งlocateกลับเมื่อคุณยังมีที่ว่างในดิสก์
tripleee

-2

รุ่นที่ปลอดภัยยิ่งกว่าการใช้ xargs และไม่เรียกซ้ำ ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done

การกรองไดเรกทอรีของเราที่นี่มีความจำเป็นเล็กน้อยเพราะ 'rm' จะไม่ลบทิ้งและสามารถลบออกได้โดยง่าย แต่ทำไมต้องใช้บางสิ่งที่จะส่งคืนข้อผิดพลาดอย่างแน่นอน


3
นี่ไม่ปลอดภัยเลยและไม่ทำงานกับชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่เพื่อชี้ให้เห็นกรณีมุมที่ชัดเจนหนึ่งกรณี การแยกวิเคราะห์lsเป็น antipattern ทั่วไปที่ควรหลีกเลี่ยงอย่างแน่นอนและเพิ่มจำนวนข้อผิดพลาดเพิ่มเติมที่นี่ grep | grepเป็นเพียงไม่สง่างามมาก
tripleee

อย่างไรก็ตามมันไม่ได้เป็นเช่นนี้เป็นปัญหาใหม่และแปลกใหม่ซึ่งต้องใช้วิธีแก้ปัญหาที่ซับซ้อน คำตอบfindที่ดีและมีเอกสารที่ดีและที่อื่น ๆ ดูเช่นmywiki.wooledge.orgสำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้และหัวข้อที่เกี่ยวข้อง
tripleee

-2

การใช้ GNU parallel ( sudo apt install parallel) นั้นง่ายมาก

มันรันคำสั่งมัลติเธรดโดยที่ '{}' เป็นอาร์กิวเมนต์ที่ส่งผ่าน

เช่น

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


ฉันไม่รู้ แต่ฉันเดาว่าเพราะการส่งเอาต์พุตของlsคำสั่งโดยตรงไปยังคำสั่งอื่นเป็นอันตราย antipattern - นั่นและความจริงที่ว่าการขยายตัวของ wildcard จะทำให้เกิดความล้มเหลวเดียวกันเมื่อดำเนินการlsตามประสบการณ์ในrmคำสั่งดั้งเดิม.
Toby Speight

สำหรับบริบทที่เห็นParsingLs และparallelทำให้บางคนที่ชอบหลีกเลี่ยงความซับซ้อนไม่สบายใจ - ถ้าคุณดูใต้กระโปรงหน้ารถ ดูกระทู้รายชื่อผู้รับจดหมายได้ที่lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.htmlระหว่าง Stephane (หนึ่งในGreybeards Unix & Linux StackExchange) และ Ole Tange (ผู้เขียนของ Parallel) xargs -Pทำให้เป็นอัมพาตเช่นกัน แต่มันทำได้ง่ายกว่าและโง่เง่าเมื่อมีชิ้นส่วนเคลื่อนไหวน้อยลงทำให้พฤติกรรมของมันง่ายต่อการคาดเดาและเหตุผลมากขึ้น
Charles Duffy

-2

สำหรับการลบ 100 ไฟล์แรก:

rm -rf 'ls | หัว -100 '


2
เป็นอันตราย (หรืออาจเป็นถ้าคุณใช้ backquotes ตามที่ตั้งใจ) - หากชื่อไฟล์ใด ๆ ที่มีอักขระ metacharacters รวมถึงช่องว่างผลลัพธ์จะไม่เป็นสิ่งที่คุณตั้งใจ
Toby Speight

-5

ตัวเลือกด้านล่างดูเหมือนจะง่ายสำหรับปัญหานี้ ฉันได้รับข้อมูลนี้จากเธรดอื่น แต่มันช่วยฉันได้

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

เพียงแค่เรียกใช้คำสั่งข้างต้นและมันจะทำงาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.