Unix - ลบไฟล์และโฟลเดอร์ยกเว้น PATTERN


10

เมื่อเร็ว ๆ นี้ฉันได้พบกับงานของการลบไฟล์ / โฟลเดอร์ทั้งหมดในไดเรกทอรียกเว้นที่ตรงกับรูปแบบเฉพาะ ดังนั้นฉันจึงทำคำสั่ง unix บรรทัดเดียวเพื่อทำงาน มันจะต้องเป็นเพียงหนึ่งบรรทัด? ฉันไม่คิด แต่มันก็เย็นกว่านี้แน่ ๆ !

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

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

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

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

การใช้รูปแบบ "foo" จะต้องลบ "first_dir" (และเป็นเนื้อหาของหลักสูตร) ​​และ "somefile.txt" ( ไม่ใช่ "anotherfile.txt" หรือ "morefiles.log")

คำถามมีวิธีที่ดีกว่า (ฉลาดกว่าและถูกต้อง) ในการทำสิ่งนี้ให้สำเร็จหรือไม่?


แก้ไข:
เมื่อไม่นานมานี้ข้าพเจ้าได้ทราบว่า "พบ" อาจเป็นตัวเลือกที่ดีกว่า :

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

โซลูชันนี้จัดการเส้นทางที่มีอักขระป้อนบรรทัดได้อย่างถูกต้อง


แก้ไข: เปลี่ยน "the_dir" ที่ผิดพลาดให้ถูกต้อง "first_dir"
joxl

คำตอบ:


10

ตัวอย่างต่อไปนี้มีechoคำนำหน้าเพื่อให้คุณสามารถทดสอบรูปแบบก่อนใช้งานจริง ลบเพื่อเปิดใช้งานecho rm -rfแทนrm -riการแจ้งให้ยืนยัน

kshมีส่วนขยายการจับคู่เชิงลบเป็นแบบโค้ง:

# ksh
echo rm -rf !(*foo*)

ไวยากรณ์เดียวกันนี้มีอยู่ในbashหากคุณตั้งค่าextglobตัวเลือก:

# bash
shopt -s extglob
echo rm -rf !(*foo*)

zshมีไวยากรณ์ของตัวเองสำหรับสิ่งนี้:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

นอกจากนี้ยังสามารถใช้ไวยากรณ์ksh -style:

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))

เรียบง่ายสง่างามจัดการกับอักขระตัวดึงบรรทัด (เกือบ) หนึ่งบรรทัด (ฉันเพิ่มลงshopt -s extglobในไฟล์. bashrc ของฉันแล้วดังนั้นตอนนี้จึงเป็นไฟล์เดียว) ที่สมบูรณ์แบบ! ฉันสัญญาว่าจะอัปโหลดทันทีที่ชื่อเสียงของฉันอนุญาต
joxl

7

นี่คือfindทางออกอื่น ฉันไม่แน่ใจว่าสิ่งนี้มีข้อได้เปรียบที่แท้จริงมากกว่าของคุณ แต่ไม่จำเป็นxargsและอนุญาตให้มีความเป็นไปได้น้อยมากที่ * ขยายชื่อมากเกินไป

find . -maxdepth 1 ! -name PATTERN -type f -delete

ฉันยังเพิ่ม-type fเพื่อที่จะไม่พยายามลบไดเรกทอรี

คำเตือน: -deleteมีประสิทธิภาพ ฉันให้สิทธิ์การทดสอบไฟล์หนึ่งของฉัน 0 และคำสั่งข้างต้นลบมันโดยไม่ลังเล


6
ทดสอบก่อน แทนที่-deleteด้วย-printเพื่อดูว่ามันค้นหาไฟล์ที่คุณต้องการหรือไม่ หากทุกอย่างดีใช้ประวัติคำสั่ง (เช่นกดปุ่มขึ้น) เพื่อกลับไปที่คำสั่งก่อนหน้าเพื่อให้แน่ใจว่าคุณกำลังทำงานกับตัวกรองการค้นหาแบบเดียวกัน
Doug Harris

อ่านอย่างละเอียดทราบว่าผมไม่ต้องการที่จะลบไดเรกทอรี และfind -deleteจะไม่ลบไดเรกทอรีที่ไม่ว่างเปล่า ยังสังเกตเห็นว่าfind -maxdepth 1ตรงกับ "." (ไดเรกทอรีปัจจุบัน) ซึ่งไม่ดีจริงๆ แม้ว่าฉันจะชอบความคิดของคุณในการกำจัดxargsแต่ก็อาจใช้find -execวิธีอื่นก็ได้ find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
joxl

0

คุณสามารถใช้ตัวอย่างสคริปต์ rebol นี้http://askcodegeneration.com/keep-subversion/ แทนซึ่งเข้าใจได้ง่ายกว่ามากและสามารถจัดการการโต้ตอบของผู้ใช้เช่นการเลือกโฟลเดอร์และการยืนยัน:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.