ลบทั้งหมดยกเว้นไฟล์ X ล่าสุดใน bash


157

มีวิธีที่ง่ายในสภาพแวดล้อมระบบปฏิบัติการยูนิกซ์สวยกับมาตรฐานทุบตีเพื่อเรียกใช้คำสั่งลบทั้งหมด แต่ส่วนใหญ่ไฟล์ X ล่าสุดจากไดเรกทอรีหรือไม่?

หากต้องการยกตัวอย่างที่เป็นรูปธรรมมากขึ้นลองจินตนาการถึงงาน cron ที่เขียนไฟล์ (เช่นไฟล์บันทึกหรือการสำรองข้อมูล tar-ed up) ไปยังไดเรกทอรีทุกชั่วโมง ฉันต้องการวิธีที่จะให้งาน cron อื่นทำงานซึ่งจะลบไฟล์ที่เก่าที่สุดในไดเรกทอรีนั้นจนกว่าจะมีค่าน้อยกว่า 5

และเพียงแค่ต้องมีความชัดเจนมีเพียงหนึ่งแฟ้มปัจจุบันก็ไม่ควรจะถูกลบออก

คำตอบ:


117

ปัญหาที่เกิดขึ้นกับคำตอบที่มีอยู่:

  • ไม่สามารถจัดการชื่อไฟล์ด้วยช่องว่างที่ฝังหรือขึ้นบรรทัดใหม่
    • ในกรณีของการแก้ปัญหาที่เรียกใช้rmโดยตรงในการทดแทนคำสั่งที่ไม่มีการอ้างอิง ( rm `...`) มีความเสี่ยงเพิ่มขึ้นจากการวนซ้ำแบบไม่ตั้งใจ
  • ไม่สามารถที่จะแยกแยะความแตกต่างระหว่างไฟล์และไดเรกทอรี (เช่นถ้าไดเรกทอรีที่เกิดขึ้นเป็นหนึ่งใน 5 รายการระบบแฟ้มการแก้ไขมากที่สุดเมื่อเร็ว ๆ นี้คุณมีประสิทธิภาพต้องการรักษาน้อยกว่า 5 ไฟล์และการประยุกต์ใช้rmเพื่อไดเรกทอรีจะล้มเหลว)

คำตอบของ wnoiseแก้ปัญหาเหล่านี้ได้ แต่วิธีแก้ปัญหาคือGNU -specific (และค่อนข้างซับซ้อน)

นี่เป็นทางปฏิบัติวิธีการแก้ปัญหาที่สอดคล้องกับ POSIXที่มาพร้อมกับเพียงหนึ่งคำเตือน : มันไม่สามารถจัดการกับชื่อไฟล์ฝังบรรทัดใหม่ - แต่ฉันไม่ได้พิจารณาว่าเป็นความกังวลที่แท้จริงของโลกสำหรับคนส่วนใหญ่

สำหรับบันทึกต่อไปนี้เป็นคำอธิบายว่าทำไมโดยทั่วไปแล้วไม่ใช่ความคิดที่ดีที่จะแยกวิเคราะห์lsเอาต์พุต: http://mywiki.wooledge.org/ParsingLs

ls -tp | grep -v '/$' | tail -n +6 | xargs -I {} rm -- {}

ด้านบนไม่มีประสิทธิภาพเนื่องจากxargsต้องเรียกใช้rmหนึ่งครั้งสำหรับแต่ละชื่อไฟล์
แพลตฟอร์มของคุณxargsอาจอนุญาตให้คุณแก้ปัญหานี้:

หากคุณมีGNU xargsให้ใช้-d '\n'ซึ่งจะxargsพิจารณาแต่ละบรรทัดอินพุตว่ามีอาร์กิวเมนต์แยกต่างหาก แต่จะส่งผ่านอาร์กิวเมนต์ได้มากเท่าที่จะพอดีกับบรรทัดคำสั่งพร้อมกัน:

ls -tp | grep -v '/$' | tail -n +6 | xargs -d '\n' -r rm --

-r( --no-run-if-empty) รับรองrmว่าไม่ได้เรียกใช้หากไม่มีอินพุต

หากคุณมีBSD xargs (รวมถึงmacOS ) คุณสามารถใช้-0เพื่อจัดการNULอินพุตแยกหลังจากการขึ้นบรรทัดใหม่เป็นNUL( 0x0) ตัวอักษรแรกซึ่งจะผ่าน (โดยทั่วไป) ชื่อไฟล์ทั้งหมดในครั้งเดียว (จะทำงานกับ GNU xargs):

ls -tp | grep -v '/$' | tail -n +6 | tr '\n' '\0' | xargs -0 rm --

คำอธิบาย:

  • ls -tpพิมพ์ชื่อของรายการระบบแฟ้มเรียงตามวิธีการที่เมื่อเร็ว ๆ นี้พวกเขาได้รับการแก้ไขในลำดับถัดลง (แก้ไขมากที่สุดเมื่อเร็ว ๆ นี้รายการแรก) ( -t) กับไดเรกทอรีพิมพ์ต่อท้าย/เพื่อทำเครื่องหมายพวกเขาเป็นเช่นนี้ ( -p)
  • grep -v '/$'จากนั้นกำจัดไดเรกทอรีออกจากรายการที่เกิดขึ้นโดยเว้น-vบรรทัด( ) ที่มีส่วนท้าย/( /$)
    • ข้อแม้ : ตั้งแต่symlink ที่ชี้ไปยังไดเรกทอรีเป็นเทคนิคที่ไม่ตัวเองไดเรกทอรี symlinks ดังกล่าวจะไม่ได้รับการยกเว้น
  • tail -n +6ข้ามแรก5รายการในรายชื่อในผลกลับมาทั้งหมดแต่ 5 ไฟล์ส่วนใหญ่เมื่อเร็ว ๆ นี้มีการปรับเปลี่ยนถ้ามี
    ทราบว่าในการที่จะไม่รวมNไฟล์จะต้องส่งผ่านไปยังN+1tail -n +
  • xargs -I {} rm -- {}(และรูปแบบของไฟล์) จากนั้นจะเรียกใช้rmไฟล์เหล่านี้ทั้งหมด ถ้ามีการแข่งขันที่ทุกคนไม่มีxargsจะไม่ทำอะไร
    • xargs -I {} rm -- {}กำหนด{}ตัวแทนที่แสดงถึงแต่ละบรรทัดอินพุตโดยรวมดังนั้นrmจะถูกเรียกใช้หนึ่งครั้งสำหรับแต่ละบรรทัดอินพุต แต่ด้วยชื่อไฟล์ที่มีการเว้นวรรคแบบฝังที่จัดการอย่างถูกต้อง
    • --ในทุกกรณีเพื่อให้แน่ใจว่าชื่อไฟล์ใด ๆ ที่เกิดขึ้นจะเริ่มต้นด้วย-ไม่ได้เข้าใจผิดว่าเป็นตัวเลือกrmโดย

ความแตกต่างของปัญหาดั้งเดิมในกรณีที่ไฟล์ที่ตรงกันนั้นต้องถูกประมวลผลแยกกันหรือรวบรวมในเชลล์อาเรย์ :

# One by one, in a shell loop (POSIX-compliant):
ls -tp | grep -v '/$' | tail -n +6 | while IFS= read -r f; do echo "$f"; done

# One by one, but using a Bash process substitution (<(...), 
# so that the variables inside the `while` loop remain in scope:
while IFS= read -r f; do echo "$f"; done < <(ls -tp | grep -v '/$' | tail -n +6)

# Collecting the matches in a Bash *array*:
IFS=$'\n' read -d '' -ra files  < <(ls -tp | grep -v '/$' | tail -n +6)
printf '%s\n' "${files[@]}" # print array elements

2
ดีกว่าคำตอบอื่น ๆ ส่วนใหญ่ที่นี่ดังนั้นฉันยินดีที่จะให้การสนับสนุนแม้ในขณะที่ฉันพิจารณาว่าการขึ้นบรรทัดใหม่เป็นสิ่งที่ต้องทำด้วยความระมัดระวัง
ชาร์ลส์ดัฟฟี่

2
หากคุณlsไม่ได้อยู่ในไดเรกทอรีปัจจุบันเส้นทางไปยังไฟล์จะมี '/' ซึ่งหมายความว่าgrep -v '/'จะไม่ตรงกับสิ่งใด ฉันเชื่อว่าgrep -v '/$'เป็นสิ่งที่คุณต้องการยกเว้นไดเรกทอรีเท่านั้น
waldol1

1
@ waldol1: ขอบคุณ; ฉันได้อัปเดตคำตอบเพื่อรวมข้อเสนอแนะของคุณซึ่งทำให้grepคำสั่งมีแนวคิดชัดเจนขึ้น อย่างไรก็ตามโปรดทราบว่าปัญหาที่คุณอธิบายจะไม่ปรากฏขึ้นพร้อมกับเส้นทางไดเรกทอรีเดียว เช่นls -p /private/varยังคงพิมพ์เฉพาะชื่อไฟล์เพียง เฉพาะในกรณีที่คุณผ่านหลายข้อโต้แย้งไฟล์ (โดยทั่วไปผ่าน glob ก) คุณจะเห็นเส้นทางที่เกิดขึ้นจริงในการส่งออก; เช่นls -p /private/var/*(และคุณจะยังดูเนื้อหาของไดเรกทอรีย่อยจับคู่ถ้าคุณยังรวมถึง-d)
mklement0

108

ลบทั้งหมดยกเว้น 5 (หรือจำนวนเท่าใดก็ได้) ของไฟล์ล่าสุดในไดเรกทอรี

rm `ls -t | awk 'NR>5'`

2
ฉันต้องการสิ่งนี้เพื่อพิจารณาไฟล์เก็บถาวรของฉันเท่านั้น การเปลี่ยนแปลงls -tไปls -td *.bz2
เจมส์ T ปราดเปรื่อง

3
ฉันใช้นี้ไดเรกทอรีโดยเปลี่ยนไป RM -rf ls -t | awk 'NR>1'(ฉันต้องการเพียงล่าสุด) ขอบคุณ!
lohiaguitar91

11
ls -t | awk 'NR>5' | xargs rm -f หากคุณต้องการไพพ์และคุณต้องระงับข้อผิดพลาดหากไม่มีสิ่งใดถูกลบ
H2ONaCl

16
กระชับและสามารถอ่านได้บางที แต่เป็นอันตรายต่อการใช้งาน ถ้าพยายามที่จะลบไฟล์ที่สร้างขึ้นด้วยtouch 'hello * world'นี้จะลบทุกอย่างในไดเรกทอรีปัจจุบัน
ชาร์ลส์ดัฟฟี่

1
แม้ว่านี่จะได้รับคำตอบในปี 2008 แต่มันก็ทำงานได้อย่างมีเสน่ห์และสิ่งที่ฉันต้องการคือเพียงแค่ลบการสำรองข้อมูลเก่าออกจากไดเรกทอรีเฉพาะ น่ากลัว
Rens Tillmann

86
(ls -t|head -n 5;ls)|sort|uniq -u|xargs rm

รุ่นนี้รองรับชื่อที่มีช่องว่าง:

(ls -t|head -n 5;ls)|sort|uniq -u|sed -e 's,.*,"&",g'|xargs rm

20
คำสั่งนี้จะจัดการไฟล์ที่มีช่องว่างในชื่อไม่ถูกต้อง
tylerl

5
(ls -t|head -n 5;ls)เป็นกลุ่มคำสั่ง มันพิมพ์ 5 ไฟล์ล่าสุดสองครั้ง sortใส่บรรทัดที่เหมือนกันเข้าด้วยกัน uniq -uลบรายการที่ซ้ำดังนั้นทั้งหมดยกเว้นไฟล์ล่าสุด 5 ไฟล์จะยังคงอยู่ xargs rmเรียกร้องrmของเขาแต่ละคน
Fabien

15
จะเป็นการลบไฟล์ทั้งหมดของคุณหากคุณมี 5 หรือน้อยกว่า! เพิ่ม--no-run-if-emptyไปxargsในขณะที่(ls -t|head -n 5;ls)|sort|uniq -u|xargs --no-run-if-empty rmโปรดอัปเดตคำตอบ
Gonfi den Tschal

3
แม้แต่คนที่ "รองรับชื่อที่มีช่องว่าง" ก็เป็นอันตราย พิจารณาชื่อที่มีเครื่องหมายคำพูด: touch 'foo " bar'จะสลัดคำสั่งที่เหลือทั้งหมด
Charles Duffy

2
... มันปลอดภัยในการใช้xargs -d $'\n'มากกว่าคำพูดฉีดเข้าไปในเนื้อหาของคุณแม้ว่า NUL-delimiting สตรีมใส่ (ซึ่งจะต้องมีการใช้สิ่งอื่น ๆ นอกเหนือlsไปจริงๆทำขวา) เป็นตัวเลือกที่เหมาะ
Charles Duffy

59

ตัวแปรที่เรียบง่ายของคำตอบของ thelsdj:

ls -tr | head -n -5 | xargs --no-run-if-empty rm 

LS -tr แสดงไฟล์ทั้งหมดที่เก่าแก่ที่สุดเป็นครั้งแรก (t-ใหม่ล่าสุดครั้งแรก -r กลับ)

head -n -5 แสดงทั้งหมดยกเว้น 5 บรรทัดสุดท้าย (เช่น 5 ไฟล์ใหม่ล่าสุด)

xargs rm เรียก rm สำหรับแต่ละไฟล์ที่เลือก


15
จำเป็นต้องเพิ่ม - ไม่เรียกใช้ถ้าว่างไปยัง xargs เพื่อที่จะไม่ล้มเหลวเมื่อมีไฟล์น้อยกว่า 5 ไฟล์
ทอม

ls -1tr | หัว -n -5 | xargs rm <---------- คุณต้องเพิ่ม -1 ลงใน ls มิฉะนั้นคุณจะไม่ได้รับลิสต์เอาต์พุตสำหรับส่วนหัวเพื่อให้ทำงานได้อย่างถูกต้อง
Al Joslin

3
@AlJoslin -1เป็นค่าเริ่มต้นเมื่อเอาต์พุตเป็นไพพ์ไลน์ดังนั้นจึงไม่จำเป็นที่นี่ สิ่งนี้มีปัญหาที่ใหญ่กว่ามากซึ่งเกี่ยวข้องกับพฤติกรรมเริ่มต้นของxargsเมื่อแยกชื่อด้วยช่องว่างอัญประกาศ
ชาร์ลส์ดัฟฟี่

ดูเหมือนว่า--no-run-if-emptyไม่เป็นที่รู้จักในเปลือกของฉัน ฉันใช้ Cmder บน windows
StayFoolish

อาจต้องใช้-0ตัวเลือกหากชื่อไฟล์อาจมีช่องว่าง ยังไม่ได้ทดสอบ แหล่งที่มา
Keith

18
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -r -z -n | awk 'BEGIN { RS="\0"; ORS="\0"; FS="" } NR > 5 { sub("^[0-9]*(.[0-9]*)? ", ""); print }' | xargs -0 rm -f

ต้องการ GNU ค้นหา -printf และ GNU sort สำหรับ -z และ GNU awk สำหรับ "\ 0" และ GNU xargs สำหรับ -0 แต่จัดการกับไฟล์ด้วยการขึ้นบรรทัดใหม่หรือช่องว่าง


2
หากคุณต้องการที่จะลบไดเรกทอรีเพียงแค่เปลี่ยน -f ไปยัง -d และเพิ่ม -r เพื่อ RM หา -maxdepth 1 - พิมพ์ d -printf '% T @% p \ 0' | sort -r -z -n | awk 'BEGIN {RS = "\ 0"; ORS = "\ 0"; FS = ""} NR> 5 {sub ("^ [0-9] * (. [0-9] *)?", ""); พิมพ์} '| xargs -0 RM -rf
alex

1
ฉันรู้สึกประหลาดใจในความซับซ้อน (หรือความจำเป็น) ของawkตรรกะ ฉันไม่มีข้อกำหนดบางอย่างในคำถามของ OP ที่ทำให้จำเป็นหรือไม่
ชาร์ลส์ดัฟฟี่

@Charles ดัฟฟี่: ย่อย () เอาประทับเวลาซึ่งเป็นสิ่งที่จะถูกจัดเรียงบน การประทับเวลาที่สร้างโดย "% T @" อาจรวมถึงส่วนที่เป็นเศษส่วน การแยกพื้นที่ด้วย FS แบ่งเส้นทางด้วยช่องว่างที่ฝัง ฉันคิดว่าการลบผ่านการทำงานในพื้นที่แรก แต่เกือบเป็นเรื่องยากที่จะอ่าน ไม่สามารถตั้งค่าตัวคั่น RS และ ORS บนบรรทัดคำสั่งได้เนื่องจากเป็น NUL
wnoise

1
@wnoise วิธีการปกติของฉันคือการไพพ์ไปสู่ ​​shell while read -r -d ' '; IFS= -r -d ''; do ...loop - การอ่านครั้งแรกสิ้นสุดลงบนช่องว่างในขณะที่วิธีที่สองไปที่ NUL
Charles Duffy

@Charles Duffy: ฉันมักจะโกงเปลือกดิบบางทีอาจเป็นเพราะความกังวลของไบเซนไทน์ ตอนนี้ฉันคิดว่า GNU sed -z -e 's/[^ ]* //; 1,5d'ชัดเจนที่สุดแล้ว (หรืออาจจะsed -n -z -e 's/[^ ]* //; 6,$p'.
wnoise

14

คำตอบทั้งหมดเหล่านี้ล้มเหลวเมื่อมีไดเรกทอรีในไดเรกทอรีปัจจุบัน นี่คือสิ่งที่ใช้งานได้:

find . -maxdepth 1 -type f | xargs -x ls -t | awk 'NR>5' | xargs -L1 rm

นี้:

  1. ทำงานเมื่อมีไดเรกทอรีในไดเรกทอรีปัจจุบัน

  2. พยายามลบแต่ละไฟล์แม้ว่าไฟล์ก่อนหน้าจะไม่สามารถลบออกได้ (เนื่องจากสิทธิ์ ฯลฯ )

  3. ล้มเหลวอย่างปลอดภัยเมื่อจำนวนไฟล์ในไดเรกทอรีปัจจุบันมากเกินไปและxargsโดยปกติแล้วจะทำให้คุณเมา ( -x)

  4. ไม่รองรับช่องว่างในชื่อไฟล์ (บางทีคุณใช้ระบบปฏิบัติการที่ไม่ถูกต้องหรือ)


5
เกิดอะไรขึ้นถ้าfindชื่อไฟล์ผลตอบแทนที่มากขึ้นกว่าที่สามารถส่งผ่านบนบรรทัดคำสั่งเดียวls -t? (คำแนะนำ: คุณจะได้รับการรันจำนวนls -tมากซึ่งแต่ละรายการจะเรียงลำดับแบบแยกกันแทนที่จะมีลำดับการจัดเรียงที่ถูกต้องทั่วโลกดังนั้นคำตอบนี้จะไม่ถูกต้องเมื่อใช้กับไดเรกทอรีที่มีขนาดใหญ่พอ)
Charles Duffy

12
ls -tQ | tail -n+4 | xargs rm

แสดงรายการชื่อไฟล์ตามเวลาที่แก้ไขโดยระบุชื่อไฟล์แต่ละชื่อ ยกเว้น 3 แรก (3 ล่าสุด) ลบที่เหลืออยู่

แก้ไขหลังจากความคิดเห็นที่เป็นประโยชน์จาก mklement0 (ขอบคุณ!): แก้ไขอาร์กิวเมนต์ -n + 3 และโปรดทราบว่าสิ่งนี้จะไม่ทำงานอย่างที่คาดไว้หากชื่อไฟล์มีการขึ้นบรรทัดใหม่และ / หรือไดเรกทอรีมีไดเรกทอรีย่อย


-Qตัวเลือกที่ไม่ได้ดูเหมือนจะมีอยู่บนเครื่องของฉัน
Pierre-Adrien Buisson

4
อืมตัวเลือกที่ได้รับใน GNU utils หลัก ~ 20 ปี แต่ไม่ได้กล่าวถึงในสายพันธุ์ BSD คุณใช้ mac ไหม
ทำเครื่องหมาย

แน่นอนฉัน ไม่คิดว่าจะมีความแตกต่างสำหรับคำสั่งพื้นฐานแบบนี้จริง ๆ ระหว่างระบบที่ทันสมัย ขอบคุณสำหรับคำตอบ !
Pierre-Adrien Buisson

3
@ Mark: ++ -Qสำหรับ ใช่-Qเป็นส่วนขยาย GNU (นี่คือข้อมูลจำเพาะPOSIXls ) ข้อแม้เล็ก ๆ (ไม่ค่อยมีปัญหาในทางปฏิบัติ): -Qเข้ารหัสบรรทัดใหม่ที่ฝังอยู่ในชื่อไฟล์ว่าเป็นตัวอักษร\nซึ่งrmไม่รู้จัก หากต้องการยกเว้นแรก3ที่ต้องโต้แย้งxargs +4ในที่สุดคำเตือนที่ใช้กับคำตอบอื่น ๆ ส่วนใหญ่เช่นกันคำสั่งของคุณจะทำงานตามที่ตั้งใจไว้หากไม่มีไดเรกทอรีย่อยในไดเรกทอรีปัจจุบัน
mklement0

1
เมื่อไม่มีสิ่งใดที่จะลบคุณต้องโทร xargs พร้อม--no-run-if-emptyตัวเลือก:ls -tQ | tail -n+4 | xargs --no-run-if-empty rm
Olivier Lecrivain

8

การไม่สนใจบรรทัดใหม่คือการละเว้นความปลอดภัยและการเข้ารหัสที่ดี wnoise มีคำตอบที่ดีเท่านั้น นี่คือการเปลี่ยนแปลงของเขาที่ทำให้ชื่อไฟล์ในอาร์เรย์ $ x

while IFS= read -rd ''; do 
    x+=("${REPLY#* }"); 
done < <(find . -maxdepth 1 -printf '%T@ %p\0' | sort -r -z -n )

2
ฉันขอแนะนำให้ล้างข้อมูลIFSมิฉะนั้นคุณอาจเสี่ยงต่อการสูญเสียช่องว่างต่อท้ายจากชื่อไฟล์ สามารถกำหนดขอบเขตให้กับคำสั่ง read:while IFS= read -rd ''; do
Charles Duffy

1
ทำไม"${REPLY#* }"?
msciwoj

4

หากชื่อไฟล์ไม่มีช่องว่างสิ่งนี้จะทำงาน:

ls -C1 -t| awk 'NR>5'|xargs rm

หากชื่อไฟล์มีช่องว่างบางอย่างเช่น

ls -C1 -t | awk 'NR>5' | sed -e "s/^/rm '/" -e "s/$/'/" | sh

ตรรกะพื้นฐาน:

  • รับรายชื่อของไฟล์ตามลำดับเวลาหนึ่งคอลัมน์
  • รับทั้งหมดยกเว้น 5 ตัวแรก (n = 5 สำหรับตัวอย่างนี้)
  • เวอร์ชันแรก: ส่งไปที่ rm
  • รุ่นที่สอง: สร้างสคริปต์ที่จะลบออกอย่างเหมาะสม

อย่าลืมwhile readเคล็ดลับในการรับมือกับช่องว่าง: ls -C1 -t | awk 'NR>5' | while read d ; do rm -rvf "$d" ; done
pinkeen

1
@pinkeen ไม่ค่อยปลอดภัยตามที่ระบุไว้ while IFS= read -r dจะดีกว่านี้เล็กน้อย - การ-rป้องกันตัวอักษรแบ็กสแลชไม่ให้ถูกบริโภคreadและIFS=ป้องกันการตัดส่วนที่เหลือโดยอัตโนมัติของช่องว่างต่อท้าย
Charles Duffy

4
BTW หากมีใครกังวลเกี่ยวกับชื่อไฟล์ที่เป็นศัตรูนี่เป็นวิธีที่อันตรายอย่างยิ่ง พิจารณาไฟล์ที่สร้างขึ้นด้วยtouch $'hello \'$(rm -rf ~)\' world'; คำพูดตามตัวอักษรในชื่อไฟล์จะตอบโต้คำพูดตามตัวอักษรที่คุณเพิ่มเข้าไปsedซึ่งจะส่งผลให้โค้ดภายในชื่อไฟล์ถูกเรียกใช้งาน
ชาร์ลส์ดัฟฟี่

1
(เพื่อความชัดเจน "สิ่งนี้" ข้างต้นได้อ้างถึง| shแบบฟอร์มซึ่งเป็นสิ่งที่มีช่องโหว่การฉีดเชลล์)
ชาร์ลส์ดัฟฟี่

2

ด้วย zsh

สมมติว่าคุณไม่สนใจไดเรกทอรีที่มีอยู่และคุณจะไม่มีไฟล์มากกว่า 999 ไฟล์ (เลือกจำนวนที่มากขึ้นถ้าคุณต้องการหรือสร้างลูปสักครู่)

[ 6 -le `ls *(.)|wc -l` ] && rm *(.om[6,999])

ใน*(.om[6,999])การ.แฟ้มหมายที่oหมายถึงลำดับการจัดเรียงขึ้นmหมายถึงวันที่ของการปรับเปลี่ยน (ใส่aสำหรับเวลาในการเข้าถึงหรือcการเปลี่ยนแปลง inode) ที่[6,999]เลือกช่วงของไฟล์เพื่อไม่ rm 5 ครั้งแรก


ที่น่าสนใจ แต่สำหรับชีวิตของฉันฉันไม่สามารถได้รับการคัดเลือกรอบคัดเลือก ( om) ในการทำงาน (การเรียงลำดับใด ๆ ที่ฉันพยายามแสดงให้เห็นว่าไม่มีผลใด ๆ - ทั้งบน OSX 10.11.2 (ลองด้วย zsh 5.0.8 และ 5.1.1) , และบน Ubuntu 14.04 (zsh 5.0.2)) - ฉันจะพลาดอะไรไปบ้าง? ในฐานะที่เป็นปลายทางของช่วง: ไม่จำเป็นต้องยากรหัสมันเพียงแค่ใช้ในการอ้างถึงรายการสุดท้ายจึงรวมถึงไฟล์ที่เหลือทั้งหมด:-1 [6,-1]
mklement0

2

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

for F in $(find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n' | sort -r -z -n | tail -n+5 | awk '{ print $2; }'); do rm $F; done

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

find . -maxdepth 1 -type f -name "*_srv_logs_*.tar.gz" -printf '%T@ %p\n'

ถัดไปเรียงลำดับตามการประทับเวลา:

sort -r -z -n

จากนั้นให้ปิด 4 ไฟล์ล่าสุดจากรายการ:

tail -n+5

คว้าคอลัมน์ที่ 2 (ชื่อไฟล์ไม่ใช่เวลาประทับ):

awk '{ print $2; }'

แล้วห่อสิ่งนั้นให้เป็นคำสั่ง

for F in $(); do rm $F; done

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


1

พบ cmd ที่น่าสนใจใน Sed-Onliners - ลบ 3 บรรทัดสุดท้าย - เหมาะสำหรับวิธีอื่นในการสกินแมว (ไม่เป็นไร) แต่เป็นแนวคิด:

 #!/bin/bash
 # sed cmd chng #2 to value file wish to retain

 cd /opt/depot 

 ls -1 MyMintFiles*.zip > BigList
 sed -n -e :a -e '1,2!{P;N;D;};N;ba' BigList > DeList

 for i in `cat DeList` 
 do 
 echo "Deleted $i" 
 rm -f $i  
 #echo "File(s) gonzo " 
 #read junk 
 done 
 exit 0

1

ลบทั้งหมดยกเว้น 10 ไฟล์ล่าสุด (ล่าสุดล่าสุด)

ls -t1 | head -n $(echo $(ls -1 | wc -l) - 10 | bc) | xargs rm

หากไฟล์น้อยกว่า 10 ไฟล์จะไม่มีการลบไฟล์ใด ๆ และคุณจะมี: ส่วนหัวข้อผิดพลาด: จำนวนบรรทัดผิดกฎหมาย - 0

หากต้องการนับไฟล์ด้วยทุบตี


1

ฉันต้องการโซลูชันที่สง่างามสำหรับ busybox (เราเตอร์) โซลูชั่น xargs หรือ array ทั้งหมดไม่มีประโยชน์กับฉัน - ไม่มีคำสั่งดังกล่าวให้ใช้งาน find และ mtime ไม่ใช่คำตอบที่ถูกต้องเนื่องจากเรากำลังพูดถึง 10 รายการและไม่จำเป็นต้อง 10 วัน คำตอบของ Espo นั้นสั้นที่สุดและสะอาดที่สุด

ข้อผิดพลาดเกี่ยวกับช่องว่างและเมื่อไม่มีไฟล์ที่จะถูกลบทั้งคู่ก็สามารถแก้ไขได้ด้วยวิธีมาตรฐาน:

rm "$(ls -td *.tar | awk 'NR>7')" 2>&-

Bit รุ่นทางการศึกษามากขึ้น: เราสามารถทำได้ทุกอย่างถ้าเราใช้ awk แตกต่างกัน โดยปกติฉันใช้วิธีนี้ในการส่งผ่าน (กลับ) ตัวแปรจาก awk ไปยัง sh เมื่อเราอ่านตลอดเวลาที่ไม่สามารถทำได้ฉันขอแตกต่าง: นี่คือวิธีการ

ตัวอย่างของไฟล์. tar ที่ไม่มีปัญหาเกี่ยวกับช่องว่างในชื่อไฟล์ ในการทดสอบให้แทนที่ "rm" ด้วย "ls"

eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}')

คำอธิบาย:

ls -td *.tarแสดงรายการไฟล์. tar ทั้งหมดที่เรียงลำดับตามเวลา หากต้องการใช้กับไฟล์ทั้งหมดในโฟลเดอร์ปัจจุบันให้ลบส่วน "d * .tar"

awk 'NR>7... ข้าม 7 บรรทัดแรก

print "rm \"" $0 "\"" สร้างบรรทัด: rm "ชื่อไฟล์"

eval รันมัน

เนื่องจากเรากำลังใช้rmงานอยู่ฉันจะไม่ใช้คำสั่งด้านบนในสคริปต์! การใช้งานที่ชาญฉลาดคือ:

(cd /FolderToDeleteWithin && eval $(ls -td *.tar | awk 'NR>7 { print "rm \"" $0 "\""}'))

ในกรณีของการใช้ls -tคำสั่งจะไม่ทำอันตรายใด ๆ ในตัวอย่างที่โง่เช่น: และtouch 'foo " bar' touch 'hello * world'ไม่ใช่ว่าเราเคยสร้างไฟล์ด้วยชื่อดังกล่าวในชีวิตจริง!

sidenote หากเราต้องการส่งตัวแปรไปยัง sh ด้วยวิธีนี้เราก็จะปรับเปลี่ยนการพิมพ์ (แบบง่ายไม่มีช่องว่างยอมรับ):

print "VarName="$1

การตั้งค่าตัวแปรค่าของVarName $1สามารถสร้างตัวแปรหลายตัวในครั้งเดียว สิ่งนี้VarNameกลายเป็นตัวแปร sh ปกติและสามารถใช้งานได้ตามปกติในสคริปต์หรือเชลล์หลังจากนั้น ดังนั้นในการสร้างตัวแปรด้วย awk และให้กลับไปที่เชลล์:

eval $(ls -td *.tar | awk 'NR>7 { print "VarName=\""$1"\""  }'); echo "$VarName"

0
leaveCount=5
fileCount=$(ls -1 *.log | wc -l)
tailCount=$((fileCount - leaveCount))

# avoid negative tail argument
[[ $tailCount < 0 ]] && tailCount=0

ls -t *.log | tail -$tailCount | xargs rm -f

2
xargsไม่มี-0หรืออย่างน้อยที่สุด-d $'\n'ก็ไม่น่าเชื่อถือ; สังเกตว่ามันทำงานอย่างไรกับไฟล์ที่มีช่องว่างหรือเครื่องหมายคำพูดในชื่อของมัน
Charles Duffy

0

ฉันทำสิ่งนี้เป็นสคริปเชลล์ การใช้งาน: keep NUM DIRโดยที่ NUM คือจำนวนไฟล์ที่จะเก็บและ DIR เป็นไดเรกทอรีที่จะขัด

#!/bin/bash
# Keep last N files by date.
# Usage: keep NUMBER DIRECTORY
echo ""
if [ $# -lt 2 ]; then
    echo "Usage: $0 NUMFILES DIR"
    echo "Keep last N newest files."
    exit 1
fi
if [ ! -e $2 ]; then
    echo "ERROR: directory '$1' does not exist"
    exit 1
fi
if [ ! -d $2 ]; then
    echo "ERROR: '$1' is not a directory"
    exit 1
fi
pushd $2 > /dev/null
ls -tp | grep -v '/' | tail -n +"$1" | xargs -I {} rm -- {}
popd > /dev/null
echo "Done. Kept $1 most recent files in $2."
ls $2|wc -l
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.