ว่าฉันต้องการคัดลอกเนื้อหาของไดเรกทอรีที่ไม่รวมไฟล์และโฟลเดอร์ที่มีชื่อมีคำว่า 'Music'
cp [exclude-matches] *Music* /target_directory
สิ่งที่ควรแทนที่ [ไม่รวมการจับคู่] เพื่อทำสิ่งนี้ให้สำเร็จ
ว่าฉันต้องการคัดลอกเนื้อหาของไดเรกทอรีที่ไม่รวมไฟล์และโฟลเดอร์ที่มีชื่อมีคำว่า 'Music'
cp [exclude-matches] *Music* /target_directory
สิ่งที่ควรแทนที่ [ไม่รวมการจับคู่] เพื่อทำสิ่งนี้ให้สำเร็จ
คำตอบ:
ใน Bash คุณสามารถทำได้โดยการเปิดใช้งานextglob
ตัวเลือกเช่นนี้ (แทนที่ls
ด้วยcp
และเพิ่มไดเรกทอรีเป้าหมายแน่นอน)
~/foobar> shopt extglob
extglob off
~/foobar> ls
abar afoo bbar bfoo
~/foobar> ls !(b*)
-bash: !: event not found
~/foobar> shopt -s extglob # Enables extglob
~/foobar> ls !(b*)
abar afoo
~/foobar> ls !(a*)
bbar bfoo
~/foobar> ls !(*foo)
abar bbar
คุณสามารถปิดใช้งาน extglob ได้ในภายหลัง
shopt -u extglob
f
ยกเว้นได้foo
อย่างไร
extglob
ตัวเลือกเปลือกจะช่วยให้คุณจับคู่รูปแบบที่มีประสิทธิภาพมากขึ้นในบรรทัดคำสั่ง
คุณเปิดด้วยและปิดด้วยshopt -s extglob
shopt -u extglob
ในตัวอย่างของคุณคุณจะเริ่ม:
$ shopt -s extglob
$ cp !(*Music*) /target_directory
ตัวดำเนินการ bing globสิ้นสุดext ที่พร้อมใช้งานเต็มรูปแบบคือ (ตัดตอนมาจาก)man bash
หากมีการเปิดใช้งานตัวเลือกเปลือก extglob โดยใช้ shopt builtin ตัวดำเนินการจับคู่รูปแบบส่วนขยายจำนวนมากจะได้รับการยอมรับ รูปแบบรายการคือรายการของหนึ่งหรือหลายรูปแบบคั่นด้วย | รูปแบบคอมโพสิตอาจเกิดขึ้นโดยใช้หนึ่งในรูปแบบย่อยดังต่อไปนี้:
- (รายการรูปแบบ)
จับคู่ศูนย์หรือหนึ่งรูปแบบของรูปแบบที่กำหนด- * (รายการรูปแบบ)
จับคู่เกิดขึ้นเป็นศูนย์ของรูปแบบที่กำหนด- + (รายการรูปแบบ)
ตรงกับหนึ่งหรือหลายรูปแบบของรูปแบบที่กำหนด- @ (รายการรูปแบบ)
ตรงกับหนึ่งในรูปแบบที่กำหนด- ! (รายการรูปแบบ)
จับคู่อะไรก็ได้ยกเว้นรูปแบบที่กำหนดอย่างใดอย่างหนึ่ง
ตัวอย่างเช่นหากคุณต้องการแสดงรายการไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันที่ไม่ใช่.c
หรือ.h
ไฟล์คุณจะทำ:
$ ls -d !(*@(.c|.h))
แน่นอนว่าเปลือกหอยปกติทำงานได้ดีดังนั้นตัวอย่างสุดท้ายอาจเขียนเป็น:
$ ls -d !(*.[ch])
.c
หรือ.h
ไฟล์เป็นไดเรกทอรี
D
D
ไม่ใช่ในทุบตี (ที่ฉันรู้) แต่:
cp `ls | grep -v Music` /target_directory
ฉันรู้ว่านี่ไม่ใช่สิ่งที่คุณกำลังมองหา แต่มันจะแก้ปัญหาตัวอย่างของคุณ
หากคุณต้องการหลีกเลี่ยงต้นทุน mem ของการใช้คำสั่ง exec ฉันเชื่อว่าคุณสามารถทำได้ดีกว่าด้วย xargs ฉันคิดว่าสิ่งต่อไปนี้เป็นทางเลือกที่มีประสิทธิภาพมากกว่า
find foo -type f ! -name '*Music*' -exec cp {} bar \; # new proc for each exec
find . -maxdepth 1 -name '*Music*' -prune -o -print0 | xargs -0 -i cp {} dest/
ในทุบตีทางเลือกที่จะshopt -s extglob
เป็นตัวแปรGLOBIGNORE
มันไม่ได้ดีกว่านี้จริงๆ แต่ฉันจำได้ง่ายกว่า
ตัวอย่างที่อาจเป็นสิ่งที่โปสเตอร์ดั้งเดิมต้องการ:
GLOBIGNORE="*techno*"; cp *Music* /only_good_music/
เมื่อเสร็จแล้วunset GLOBIGNORE
จะสามารถที่จะrm *techno*
อยู่ในไดเรกทอรีแหล่งที่มา
นอกจากนี้คุณยังสามารถใช้การfor
วนรอบง่าย ๆ:
for f in `find . -not -name "*Music*"`
do
cp $f /target/dir
done
-maxdepth 1
สำหรับการไม่เรียกซ้ำ?
find
backticks จะทำให้แตกในลักษณะที่ไม่พึงประสงค์หากพบชื่อไฟล์ที่ไม่น่าสนใจ
การตั้งค่าส่วนตัวของฉันคือการใช้ grep และคำสั่ง while สิ่งนี้ทำให้ผู้ใช้สามารถเขียนสคริปต์ที่มีประสิทธิภาพ แต่สามารถอ่านได้เพื่อให้แน่ใจว่าคุณจะทำสิ่งที่คุณต้องการ บวกโดยใช้คำสั่ง echo คุณสามารถดำเนินการแห้งก่อนที่จะดำเนินการจริง ตัวอย่างเช่น:
ls | grep -v "Music" | while read filename
do
echo $filename
done
จะพิมพ์ไฟล์ที่คุณจะทำการคัดลอก หากรายการถูกต้องขั้นตอนต่อไปคือการแทนที่คำสั่ง echo ด้วยคำสั่ง copy ดังนี้:
ls | grep -v "Music" | while read filename
do
cp "$filename" /target_directory
done
bash
คุณสามารถใช้while IFS='' read -r filename
แต่แล้วบรรทัดใหม่ยังคงมีปัญหา โดยทั่วไปจะเป็นการดีที่สุดที่จะไม่ใช้ls
ในการระบุไฟล์ เครื่องมือเช่นfind
นั้นเหมาะสมกว่า
for file in *; do case ${file} in (*Music*) ;; (*) cp "${file}" /target_directory ; echo ;; esac; done
เคล็ดลับที่ฉันไม่ได้เห็นที่นี่เลยที่ไม่ได้ใช้extglob
, find
หรือgrep
คือการรักษาทั้งสองรายชื่อไฟล์เป็นชุดและ"ต่าง"พวกเขาโดยใช้comm
:
comm -23 <(ls) <(ls *Music*)
comm
เป็นที่นิยมมากกว่าdiff
เพราะไม่มี cruft พิเศษ
ผลตอบแทนนี้ทุกองค์ประกอบของชุดที่ 1 ls
ที่มีไม่ได้ยังอยู่ในชุดที่ ls *Music*
2 สิ่งนี้ต้องการให้ทั้งสองชุดเรียงลำดับเพื่อทำงานอย่างถูกต้อง ไม่มีปัญหาสำหรับการls
และการขยายตัว glob แต่ถ้าคุณกำลังใช้สิ่งที่ต้องการให้แน่ใจว่าจะก่อให้เกิดfind
sort
comm -23 <(find . | sort) <(find . | grep -i '.jpg' | sort)
อาจมีประโยชน์
comm -23 <(ls) exclude_these.list
ทางออกหนึ่งสำหรับสิ่งนี้สามารถพบได้ด้วยการค้นหา
$ mkdir foo bar
$ touch foo/a.txt foo/Music.txt
$ find foo -type f ! -name '*Music*' -exec cp {} bar \;
$ ls bar
a.txt
การค้นหามีตัวเลือกค่อนข้างน้อยคุณสามารถระบุสิ่งที่คุณรวมและแยกออกได้อย่างชัดเจน
แก้ไข: อดัมในความคิดเห็นตั้งข้อสังเกตว่านี่เป็นแบบเรียกซ้ำ ค้นหาตัวเลือก mindepth และ maxdepth อาจมีประโยชน์ในการควบคุมสิ่งนี้
find -maxdepth 1 -not -name '*Music*'
/ target_directory
ผลงานต่อไปนี้แสดงรายการ*.txt
ไฟล์ทั้งหมดใน dir ปัจจุบันยกเว้นไฟล์ที่ขึ้นต้นด้วยตัวเลข
งานนี้ในbash
, dash
, zsh
และทุก POSIX อื่น ๆ เปลือกหอยที่เข้ากันได้
for FILE in /some/dir/*.txt; do # for each *.txt file
case "${FILE##*/}" in # if file basename...
[0-9]*) continue ;; # starts with digit: skip
esac
## otherwise, do stuff with $FILE here
done
ในบรรทัดที่หนึ่งรูปแบบ/some/dir/*.txt
จะทำให้for
ห่วงย้ำกว่าไฟล์ทั้งหมดในท้ายที่สุดของชื่อด้วย/some/dir
.txt
ในบรรทัดที่สองจะใช้คำสั่ง case เพื่อแยกไฟล์ที่ไม่ต้องการออก - ${FILE##*/}
นิพจน์ตัดองค์ประกอบของชื่อ dir ชั้นนำออกจากชื่อไฟล์ (ที่นี่/some/dir/
) เพื่อให้ patters สามารถจับคู่กับชื่อไฟล์พื้นฐานของไฟล์เท่านั้น (หากคุณกำจัดชื่อไฟล์โดยอิงตามคำต่อท้ายคุณสามารถย่อให้เหลือ$FILE
แทน)
ในบรรทัดที่สามไฟล์ทั้งหมดที่ตรงกับcase
รูปแบบ[0-9]*
) จะถูกข้ามไป ( continue
คำสั่งจะข้ามไปที่การวนซ้ำครั้งถัดไปของfor
ลูป) - หากคุณต้องการทำสิ่งที่น่าสนใจมากขึ้นที่นี่เช่นการข้ามไฟล์ทั้งหมดที่ไม่ได้ขึ้นต้นด้วยตัวอักษร (a – z) โดยใช้[!a-z]*
หรือคุณสามารถใช้รูปแบบหลายรูปแบบเพื่อข้ามชื่อไฟล์หลายประเภทเช่น[0-9]*|*.bak
การข้ามไฟล์ทั้งสอง.bak
ไฟล์ และไฟล์ที่ไม่ได้ขึ้นต้นด้วยตัวเลข
*.txt
แทนที่จะเป็นเพียงแค่*
) แก้ไขแล้ว
สิ่งนี้จะทำได้ยกเว้น 'เพลง'
cp -a ^'Music' /target
สิ่งนี้และสำหรับการยกเว้นสิ่งต่าง ๆ เช่น Music? * หรือ *? Music
cp -a ^\*?'complete' /target
cp -a ^'complete'?\* /target
cp
หน้าคู่มือใน MacOS มี-a
ตัวเลือก แต่ก็ทำอะไรบางอย่างที่แตกต่างกันอย่างสิ้นเชิง แพลตฟอร์มใดรองรับสิ่งนี้
ls /dir/*/!(base*)