สำหรับการวนซ้ำในโฟลเดอร์ที่มีอักขระ \ n อยู่ในชื่อ


9

ฉันมีบางโฟลเดอร์ที่มี\nตัวอักษรมันชื่อของพวกเขา

ตัวอย่างเช่น:

$ ls
''$'\n''Test'

อ้างถึงโฟลเดอร์ที่มีชื่อทดสอบและบรรทัดว่างเปล่าหน้าชื่อ

ดังนั้นเมื่อฉันเรียกใช้สคริปต์บางอย่างเช่นนี้ในไดเรกทอรีหลัก:

while IFS= read -r d; do 
    rmdir $d
done < <(find * -type d)

มันแสดงให้เห็น:

rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory

เพราะมันทำงานสองครั้งเปิด\nและอีกครั้งTestเพราะชื่อโฟลเดอร์มีสองบรรทัด

ดังนั้นฉันจะแก้ปัญหานี้อย่างไรสคริปต์รู้\nTestเป็นเพียงโฟลเดอร์เดียว?


3
คุณต้องใช้-print0คำสั่งของ find และ-dตัวเลือกการอ่าน ดูstackoverflow.com/a/40189667/7552
glenn jackman

1
@glennjackman กรุณาตอบ!
ของหวาน

@glennjackman ขอบคุณสำหรับการตอบกลับของคุณ แต่find * -type d -print0 | while IFS= read -d '' file ; do rmdir $file ; doneคำสั่งมีผลลัพธ์ rmdir: failed to remove 'Test': No such file or directoryนี้
Tara S Volpe

5
คุณต้องอ้างอิงตัวแปร:rmdir "$file"
glenn jackman

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

คำตอบ:


12

คุณมีเพียงคำสั่งเดียวที่นั่นดังนั้นจึงเพียงพอที่จะเรียกfindด้วยการ-execเรียกค่าสถานะrmdir:

find -depth -type d -exec rmdir {} \;

หรือใช้-deleteตัวเลือกเช่นเดียวกับใน find -type d -deleteแต่มันจะไม่ทำงานกับไดเรกทอรีที่ไม่ว่างเปล่า เพื่อที่คุณจะต้องมี-emptyธง หมายเหตุยัง-deleteหมายถึง-depthว่าอาจถูกข้ามไป ดังนั้นอีกทางเลือกที่ทำงานได้ที่ทำให้ทุกอย่างเป็นกระบวนการเดียว:

find -type d -empty -delete

rm -rf {} \;หากไดเรกทอรีไม่ว่างการใช้งาน ในการแยกเฉพาะไดเรกทอรีที่มี\nชื่อไฟล์เราสามารถรวมข้อความ ANSI-C ของ bash เข้า$'...'กับคำ-nameคัดค้าน:

find  -type d -name $'*\n*' -empty -delete

POSIX-ly เราสามารถจัดการได้ด้วยวิธีนี้:

find -depth  -type d -name "$(printf '*\n*' )" -exec rmdir {} \;

เป็นมูลค่าการกล่าวขวัญว่าหากเป้าหมายของคุณคือการลบไดเรกทอรีแล้ว-deleteก็เพียงพอ แต่ถ้าคุณต้องการรันคำสั่งในไดเรกทอรีนั้น-execเหมาะสมที่สุด

ดูสิ่งนี้ด้วย


2
... ใช้ด้วยความระมัดระวังrm -rf ; P ไม่ได้findมี-deleteตัวเลือกครับ? มันจะลบไดเรกทอรีว่างเปล่าและส่งข้อผิดพลาดสำหรับไดเรกทอรีที่ไม่ว่างเช่นrmdir- อาจมีราคาถูกกว่า
ของหวาน

3
@dessert มันทำและฉันเพิ่มเข้าไป แต่-deleteไม่ลบไดเรกทอรีที่ไม่ว่างออก ดังนั้นการใช้อย่างระมัดระวังrm -rf
Sergiy Kolodyazhnyy

6
เสมอแห้งเรียกใช้เช่นfindคำสั่งโดยไม่ต้องทำลายน้ำหนักบรรทุกใด ๆ (เช่นลบ-deleteหรือ-exec whatever \;) เพื่อตรวจสอบว่ารายชื่อของไฟล์ได้รับผลกระทบเป็นจริงที่ถูกต้องและไม่ได้มีสิ่งที่ไม่ควรได้รับการลบ ...
Byte บัญชาการ

2
นี่คือ askubuntu.com ดังนั้นเราจึงสามารถสรุปได้ findGNU ใช้-exec rmdir {} +ในการแบทช์หลาย ๆ ชุดลงในหนึ่งrmdirบรรทัดคำสั่ง และ BTW " แต่มันจะไม่ทำงานกับไดเรกทอรีที่ไม่ว่าง " นำไปใช้rmdirเช่นกันดังนั้นจึงไม่แตกต่างจาก-deleteจริง ๆ rm -rมันเป็นความแตกต่างจาก
Peter Cordes

2
find -type d -empty -delete( -deleteบอกเป็นนัย-depth)
Kevin

10

คุณสามารถใช้เปลือก globs แทนfind:

for d in */ ; do 
    rmdir "$d"
done

เชลล์ glob */ตรงกับโฟลเดอร์ทั้งหมดในไดเรกทอรีปัจจุบัน โครงสร้าง for-loop นี้จะดูแลการแยกคำที่ถูกต้องโดยอัตโนมัติ

โปรดทราบว่าขึ้นอยู่กับตัวเลือกเชลล์ของคุณสิ่งนี้อาจไม่สนใจโฟลเดอร์ที่ซ่อนอยู่ (ชื่อที่ขึ้นต้นด้วย a.) shopt -s dotglobพฤติกรรมที่สามารถเปลี่ยนแปลงเพื่อให้ตรงกับไฟล์ทั้งหมดสำหรับเซสชันปัจจุบันใช้คำสั่ง

อย่าลืมอ้างตัวแปรของคุณเสมอ


glob จะพบเฉพาะไฟล์ / ไดเรกทอรีในไดเรกทอรีปัจจุบันไม่ซ้ำแบบfindไม่
ilkkachu

1
คุณถูก @ilkkachu ที่สามารถเปลี่ยนแปลงได้โดยเปิดใช้งานตัวเลือกเชลล์ globstar shopt -s globstarและใช้**/*/glob แทน
ผู้บัญชาการ Byte

6

ทั้งคำตอบที่เขียนจนถึงการโทรrmdirหนึ่งครั้งต่อหนึ่งไดเรกทอรี แต่rmdirสามารถใช้หลายข้อโต้แย้งได้ฉันสงสัยว่า: ไม่มีวิธีที่มีประสิทธิภาพมากกว่านี้ไหม

ใคร ๆ ก็ทำได้

rmdir */

และนั่นเป็นวิธีที่ง่ายและมีประสิทธิภาพมากที่สุด แต่อาจมีข้อผิดพลาดในกรณีของไดเรกทอรีจำนวนมาก (ดูความยาวสูงสุดของอาร์กิวเมนต์บรรทัดคำสั่งใน gnome-terminal คืออะไร ) หากคุณต้องการใช้วิธีนี้ในการทำงานซ้ำให้เปิดใช้งานglobstarตัวเลือกเปลือกshopt -s globstarและใช้แทน**/*/*/

ด้วย GNU find(และถ้าเราไม่ต้องการใช้-delete) เราสามารถทำได้

find -depth -type d -exec rmdir {} +

ซึ่งสร้างบรรทัดคำสั่ง“ ในลักษณะเดียวกับที่xargsสร้างบรรทัดคำสั่ง” ( man find) แทน-depthสำหรับ-maxdepth 1ถ้าคุณไม่ต้องการให้ทำงานซ้ำ

หนึ่งในวิธีที่ยอดเยี่ยมและ IMO ได้อธิบายโดยผู้ขับรถเหล็กในคำตอบนี้ :

printf '%s\0' */ | xargs -0 rmdir

สิ่งนี้ใช้เชลล์printfบิวด์อินเพื่อสร้างรายการอาร์กิวเมนต์ที่ไม่มีการคั่นเขตจากนั้นรายการนี้จะถูกไพxargsพ์ไปที่การโทรrmdirบ่อยเท่าที่จำเป็น คุณสามารถทำให้มันทำงานซ้ำด้วยshopt -s globstarและ**/*/แทนที่จะ*/เป็นข้างต้น


2
findวนซ้ำ แต่การวนซ้ำของ OP ไม่ใช่ find -maxdepth 1 -type d -exec rmdir {} +ที่จะทำซ้ำว่าพฤติกรรมการใช้งาน ( -depthเป็นเรื่องซ้ำซ้อนในกรณีนั้น) หลอกด้วยprintfการเลียนแบบfind -print0ฉันไม่เคยเห็นมาก่อน
Peter Cordes

1
Oh! ฉันเห็นlsและwhileลูปฉันพลาดว่าพวกเขากำลังเปลี่ยนเส้นทางจากการแทนที่โปรเซสแทนด้วยfindการไพพ์lsไปสู่ลูปและไม่แสดงด้วยเหตุผลบางอย่าง นั่นfind *ถูกฝังอยู่จริงๆ ใช่มัน recursive */และจะล้มเหลวถ้ามีรายการไดเรกทอรีมากเกินไปในไดเรกทอรีรวมทั้งไดเรกทอรีไม่ใช่เพราะมันไม่ได้ใช้ find .จะดีกว่ามากเพราะfindเร็วstatกว่าการขยายตัวของ bash
Peter Cordes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.