ยกเว้นจาก * ในบรรทัดคำสั่ง


14

มีสถานการณ์มากมายที่การใช้ a *หลีกเลี่ยงไม่ได้เช่นrm -rf *ในโฟลเดอร์ที่เก็บโฟลเดอร์ย่อยและไฟล์หลายพันรายการ

แต่ถ้าคุณต้องการแยกไฟล์หรือโฟลเดอร์เพียงหนึ่งหรือสองไฟล์ออกจากrmคำสั่ง ฉัน googled ทางของรอบและพบเพียงการแก้ปัญหาที่มีความซับซ้อนค่อนข้างชอบfind . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;ตามที่ระบุไว้ที่นี่

มีความเป็นไปได้findไหมที่จะทำสิ่งนี้ในวิธีที่ง่ายกว่า - โดยไม่ต้องอ้อมว่า? เช่นrm -rf --exclude='one' --exclude='two' --exclude='three' *ใน rsync หรือเพียงแค่rm -rf -e 'one','two','three' *?

แม้อาจจะเป็นไปได้โดยทั่วไปจะไม่รวมสิ่งจาก*(คำสั่งอื่น ๆ จึงชอบcp, mv... ไม่ได้มีการดำเนินการของตัวเอง)? มีอะไรที่คล้าย*{'one','two','three'}กัน


3
หากคุณต้องการเก็บไฟล์หนึ่งหรือสองไฟล์เท่านั้นวิธีที่ง่ายที่สุดและง่ายที่สุดคือย้ายไฟล์เหล่านั้นไปยังตำแหน่งอื่นให้ล้างไฟล์ที่เหลือทั้งหมดแล้วย้ายไฟล์ที่คุณเก็บไว้กลับมา หากคุณต้องการเก็บไฟล์มากขึ้นฉันจะใช้findกับ--deleteตัวเลือก (ไม่จำเป็นต้องดำเนินการrmสำหรับแต่ละไฟล์นั่นคือค่าใช้จ่ายที่ไม่จำเป็น)
Hennes

นั่นอาจเป็นความเป็นไปได้ แต่มันก็เกือบจะซับซ้อนเหมือนที่ฉันพูดถึง ฉันรู้ว่าฉันสามารถรวมคำสั่งเพื่อสิ่งที่ต้องการmv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/threeแต่ฉันต้องการให้แก้ปัญหาไปได้ที่จะ explicitely *ยกเว้นบางอย่างจาก จะมีสถานการณ์ที่การย้ายหรือคัดลอกไฟล์ไปยังปลายทางอื่นจะไม่มีตัวเลือก
เดวิด

2
นั่นคือเหตุผลที่ฉันไม่ได้โพสต์เป็นคำตอบ เป็นวิธีที่ซับซ้อนน้อยกว่าในการทำสิ่งต่าง ๆ (สามคำสั่งที่ง่ายมากเทียบกับคำสั่งที่ค่อนข้างซับซ้อนกว่า) ฉันเกลียดความซับซ้อนร่วมกับ rm
Hennes

คำตอบ:


33

สำหรับ Bash มีตัวเลือกเชลล์ที่เรียกextglobว่าปิดใช้งานโดยค่าเริ่มต้นเพื่อให้เข้ากันได้กับไวยากรณ์เชลล์มาตรฐาน Extglobเติมเต็มไวยากรณ์โดยผู้ประกอบการเพิ่มเติมเช่น!(), ?(), @()และบางมากขึ้น

เพื่อสลับบนประเภทextglob เพื่อให้มันเปิดสำหรับชนิดของผู้ใช้ปัจจุบันshopt -s extglobecho 'shopt -s extglob' >> ~/.bashrc

สำหรับตัวอย่าง rm: ด้วยextglobคุณสามารถใช้

rm -rf !(one|two|three)

ดูเหมือนว่าเป็นทางออกที่ดี แต่ฉันก็ไม่ต้องการที่จะเปิดและปิดทุกครั้งที่ฉันเข้าสู่สถานการณ์แบบที่อธิบายไว้
เดวิด

3
@ David: คุณสามารถใส่shoptสิ่งที่คุณ~/.bashrcไม่สามารถเจ็บ
enzotib

หากฉันทำให้ถูกต้องตัวเลือก extglob จะไม่เปลี่ยนเอฟเฟกต์ของ*เลย แต่เพิ่มตัวบ่งชี้ที่คล้ายกัน (เช่น!) ที่มีฟังก์ชั่นเพิ่มเติมหรือไม่ ในกรณีนั้นมันจะเป็นทางออกที่น่ารัก
เดวิด

1
@ David: แน่นอน
choroba

1
@ David เพื่อให้เข้ากันได้กับไวยากรณ์มาตรฐานเปลือก มีสถานการณ์อื่น ๆ ที่น่าจะเปลี่ยนพฤติกรรม
gerrit

4

ฉันได้สร้างยกเว้นสำหรับสิ่งที่แน่นอน (เพราะฉันยังไม่สามารถเพิ่มเอกสาร)

ฉันมีโฟลเดอร์ดังนี้:

$ ls
a b c d

การพิมพ์except b dจะให้คุณaและc

$ except b d
ac

rmตอนนี้คุณสามารถท่อที่

except b d | xargs -0 rm

นี่อาจจำยากดังนั้นทำไมไม่เพียงสร้างสคริปต์ที่อยู่ด้านบนยกเว้นเรียกว่า rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

ในทำนองเดียวกันก็คือls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls

3
นี่เป็นสิ่งที่ดี แต่ไม่จำเป็นจริงๆเมื่อคุณรู้เกี่ยวกับ extglob - askubuntu.com/a/329233/138369
David

1

เป็นทางเลือกให้ extglob (ให้คิดว่าเป็นคำตอบที่ดีมากและทุกคนควรจะมีshopt -s extglob globstarใน .bashrc ของพวกเขา) คุณสามารถใช้$ GLOBIGNORE ตัวแปรทั่วโลก สมมติว่าคุณต้องการรับทุกไฟล์ยกเว้น 'foo.txt' และ 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

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

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

เนื่องจากนี่เป็นตัวแปรระดับโลกจึงจะส่งผลกระทบต่อทุก glob ที่คุณใช้จนกว่า $ GLOBSTAR จะไม่ถูกตั้งค่าโดยการออกจากระบบ

GLOBIGNORE=

มันจะทำงานกับสตริงตัวอักษรที่ส่งไปยังตัวแปรเท่านั้น คุณสามารถเห็นสิ่งที่ฉันหมายถึงโดยการตั้งค่า $ GLOBIGNORE และดูความแตกต่างระหว่างคำสั่งเหล่านี้:

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