Glob พร้อมลำดับตัวเลข


27

ฉันมีรายการไฟล์ pdf นี้ในไดเรกทอรี:

c0.pdf   c12.pdf  c15.pdf  c18.pdf  c20.pdf  c4.pdf  c7.pdf
c10.pdf  c13.pdf  c16.pdf  c19.pdf  c2.pdf   c5.pdf  c8.pdf
c11.pdf  c14.pdf  c17.pdf  c1.pdf   c3.pdf   c6.pdf  c9.pdf

ฉันต้องการต่อสิ่งเหล่านี้โดยใช้ ghostscript ตามลำดับตัวเลข (คล้ายกับสิ่งนี้):

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=out.pdf *.pdf

แต่ลำดับการขยายตัวของเชลล์ไม่ได้ทำซ้ำลำดับตามธรรมชาติของตัวเลข แต่ตามลำดับตัวอักษร:

$ for f in *.pdf; do echo $f; done
c0.pdf
c10.pdf
c11.pdf
c12.pdf
c13.pdf
c14.pdf
c15.pdf
c16.pdf
c17.pdf
c18.pdf
c19.pdf
c1.pdf
c20.pdf
c2.pdf
c3.pdf
c4.pdf
c5.pdf
c6.pdf
c7.pdf
c8.pdf
c9.pdf

ฉันจะบรรลุคำสั่งที่ต้องการในการขยายตัว (ถ้าเป็นไปได้โดยไม่ต้องเพิ่ม0-padding ไปยังหมายเลขในชื่อไฟล์ด้วยตนเอง)?

ฉันพบคำแนะนำในการใช้ls | sort -Vแต่ฉันไม่สามารถใช้งานได้กับกรณีการใช้งานเฉพาะของฉัน


คุณสามารถใช้ตัวเลขสองหลักในทุกกรณีดังนั้นลำดับตัวอักษรจะตรงกับลำดับตัวเลข เว้นแต่คุณต้องการทำสิ่งที่ยาก
Wildcard

1
อย่างน้อย 3 หลัก! จำ Y2K
waltinator

คำตอบ:


12

ขึ้นอยู่กับสภาพแวดล้อมของคุณคุณสามารถใช้ls -vกับ GNU coreutils เช่น:

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls -v)

หรือถ้าคุณอยู่ใน FreeBSD หรือ OpenBSD เวอร์ชันล่าสุด:

gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH -sDEVICE=pdfwrite \
   -sOutputFile=out.pdf $(ls | sort -V)

ls -vจะnatural sort of (version) numbers within textเพื่อให้สามารถใช้งานได้เช่นกัน ...
Sundeep

@Sundeep: แน่นอน แต่ดูเหมือนว่านี่จะเป็น coreutils ของ GNU เท่านั้น
Thor

ใช่ดูเหมือน GNU เฉพาะ - pubs.opengroup.org/onlinepubs/9699919799
Sundeep

1
@Sundeep: -Vคุณลักษณะของsortไม่ได้ถูกระบุโดย POSIX เช่นกัน อย่างไรก็ตามดูเหมือนว่าจะมีการแพร่กระจายไปไกลกว่าเช่น FreeBSD และ OpenBSD sortรองรับ
Thor

โอเคคุณสามารถเพิ่มรายละเอียดเหล่านี้เพื่อตอบได้หรือไม่? ฉันมาข้ามคำตอบนี้ขณะที่การค้นหาปัญหาที่คล้ายกัน (glob ในลำดับตัวเลข) และเห็นlsใช้ฉันจะตรวจสอบดูว่ามันมีตัวเลือกด้วยตัวเองแทนท่อในการเรียงลำดับ :)
Sundeep


12

หากไฟล์ทั้งหมดที่สงสัยมีคำนำหน้าเหมือนกัน (เช่นข้อความที่อยู่หน้าหมายเลขcในกรณีนี้) คุณสามารถใช้

gs   … args …   c? .pdf c ??. pdf

c?.pdfขยายไปสู่c0.pdf c1.pdfc9.pdfc??.pdfขยายเป็นc10.pdf c11.pdfc20.pdf (และมากถึงc99.pdfตามความเหมาะสม) ในขณะที่คำบรรทัดคำสั่งแต่ละคำที่มีอักขระการขยายชื่อพา ธ (s) จะถูกขยายไปยังรายการของชื่อไฟล์ที่เรียง (เรียง) ตามLC_COLLATEตัวแปรรายการที่เกิดจากการขยายตัวของสัญลักษณ์ตัวแทนที่อยู่ติดกัน (globs) จะไม่รวมกัน พวกเขาจะถูกตัดแบ่ง (ฉันดูเหมือนจะจำได้ว่ามนุษย์เปลือกหน้าเมื่อระบุไว้อย่างชัดเจน แต่ฉันไม่สามารถหาได้ในตอนนี้)

แน่นอนถ้าไฟล์ที่สามารถไปถึงคุณควรใช้c999.pdf c?.pdf c??.pdf c???.pdfเป็นที่ยอมรับอาจทำให้เกิดความเบื่อหากคุณมีตัวเลขเป็นจำนวนมาก คุณสามารถย่อมันเล็กน้อย ตัวอย่างเช่นสำหรับ (ถึง) c?{,?{,?{,?{,?}}}}.pdfตัวเลขห้าหลักคุณสามารถใช้ หากรายการชื่อไฟล์ของคุณเบาบาง (เช่นมีc0.pdfและ a c12345.pdfแต่ไม่จำเป็นต้องมีทุกตัวเลขในระหว่าง) คุณควรตั้งค่าnullglobตัวเลือก มิฉะนั้นถ้า (ตัวอย่าง) คุณไม่มีไฟล์ที่มีตัวเลขสองหลักคุณจะได้รับc??.pdfอาร์กิวเมนต์ตามตัวอักษรไปยังโปรแกรมของคุณ

หากคุณมีหลายคำนำหน้า (เช่น, และมีจำนวนหนึ่งหรือสองหลัก) คุณสามารถใช้ที่เห็นได้ชัดวิธีการบังคับเดรัจฉาน:a<number>.pdfb<number>.pdf c<number>.pdf

a?.pdf a??.pdf b?.pdf b??.pdf c?.pdf c??.pdf

{a,b,c}?{,?}.pdfหรือยุบมัน


1
นี่คือคำตอบที่ดีที่สุดเพราะมันเกินกว่าการเรียกร้องในการใช้ร่างใด ๆls, statหรือสิ่งอื่น; และยังทำงานในทุบตีตามที่ร้องขอ
Kyle

5

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

FILES="c0.pdf"
for i in $(seq 1 20); do FILES="${FILES} c${i}.pdf"; done
gs [...args...] $FILES

หากอาจมีช่องว่าง[ -f c${i}.pdf ]อาจมีการเพิ่มการตรวจสอบบางอย่าง

แก้ไขดูคำตอบนี้ตามที่คุณสามารถทำได้ (โดยใช้ Bash)

gs [..args..] c{1..20}.pdf

เป็นความคิดที่ดีที่จะอ้างอิงการอ้างอิงตัวแปรเชลล์ของคุณ (เช่น"$FILES"และ"$i") เว้นแต่คุณจะไม่มีเหตุผลที่ดีที่จะทำและคุณแน่ใจว่าคุณรู้ว่าคุณกำลังทำอะไรอยู่ (ในทางตรงกันข้ามในขณะที่เครื่องหมายวงเล็บอาจมีความสำคัญ แต่ก็ไม่สำคัญเท่าเครื่องหมายคำพูดดังนั้น"c$i.pdf"จะดีพอ) คำสั่งเช่นที่มีรายการไฟล์ที่คั่นด้วยช่องว่างอาจดูเหมือนเป็นเหตุผลที่ดี ใช้โดยไม่ต้องอ้างอิง (เพราะจะไม่ทำงานในบริบทนั้น) … (ต่อ)gs  [ …args… ]  $FILES$FILES$FILES"$FILES"
G-Man กล่าวว่า 'Reinstate Monica'

(ต่อ) ... แต่เห็นความปลอดภัยของการลืมอ้างตัวแปรใน bash / POSIX เชลล์โดยเฉพาะคำตอบของฉันสำหรับบันทึกเกี่ยวกับวิธีจัดการตัวแปรหลายคำเป็นอาร์เรย์ใน bash (เช่นFILES=("c0.pdf")และFILES+=("c$i.pdf")); นอกจากนี้ยังมีคำตอบนี้ซึ่งใช้เทคนิคที่ผมขอแนะนำให้
G-Man กล่าวว่า 'Reinstate Monica'

1

เพียงแค่อ้างข้อความและแก้ไขคำตอบของ ธ ​​อร์ ... ไม่แยกวิเคราะห์ ls!

คุณสามารถใช้sort -V(ส่วนขยายที่ไม่ใช่ POSIX เพื่อจัดเรียง):

printf '%s\0' ./* | sort -zV \
    | xargs -0 gs -q -sPAPERSIZE=a4 -dNOPAUSE -dBATCH \
        -sDEVICE=pdfwrite -sOutputFile=out.pdf

(สำหรับบางคำสั่งซึ่งเห็นได้ชัดว่าสำหรับ gs นั้นเป็นคำสั่งคุณจำเป็นต้องใช้ "./ " แทนที่จะเป็น " " ... หากคำสั่งไม่ทำงานให้ลองอีกอัน)


1
ไม่ LS แจงส่งออกเป็นเพราะคำสั่ง ls แสดงชื่อไฟล์ขึ้นบรรทัดใหม่แยกออกจากกันในขณะที่การขึ้นบรรทัดใหม่เป็นที่ถูกต้องเป็นที่ใด ๆ ในชื่อไฟล์ แต่ที่นี่คุณกำลังทำสิ่งเดียวกันกับstatแต่เพิ่มปัญหาอื่น ๆ หลายคน (เช่นปัญหากับชื่อไฟล์เริ่มต้น ด้วย-ปัญหาหากมีไฟล์มากเกินไปstatเป็นคำสั่งที่ไม่ใช่แบบพกพา) และเนื่องจากคุณใช้ตัวดำเนินการแยก + glob โดยไม่ต้องปรับ IFS หรือปิดใช้งาน globs คุณจะยังคงมีปัญหาเกี่ยวกับชื่อไฟล์ที่มีช่องว่างหรือแท็บหรืออักขระตัวแทน
Stéphane Chazelas

หากต้องการใช้ GNU sort -Vน่าเชื่อถือที่คุณจะต้อง${(z)"$(printf '%s\0' * | sort -zV)"}ในzsh(แม้ว่าจะzshมี(n)การเรียงลำดับตัวเลขแล้ว) หรือในreadarray -td '' files < <(printf '%s\0' * | sort -zV) bash4.4+
Stéphane Chazelas

@ StéphaneChazelasขอบคุณและคุณมีสิทธิ์ที่จะขึ้นบรรทัดใหม่อาจเป็นข้อกังวล แต่นั่นไม่ใช่เหตุผลเดียวที่จะไม่แยกวิเคราะห์ ls และใช่ฉันขี้เกียจและไม่ได้เพิ่ม - อย่างใดอย่างหนึ่ง แต่ฉันควรจะใช้ printf ... ฉันจะเปลี่ยนมัน
ปีเตอร์

สำหรับlsคนเดียว (นั่นคือโดยไม่ต้อง -l) สิ่งที่เป็นผู้ที่กังวลอื่น ๆ ? โปรดทราบว่าจะไม่ช่วยเหลือสำหรับไฟล์ที่เรียกว่า-- -
Stéphane Chazelas

@ StéphaneChazelasมีความแตกต่างอื่น ๆ ระหว่างรุ่น ... เช่นพิมพ์บางอย่าง "รวม 0" ที่นั่นและรุ่นใหม่ล่าสุดของ ls ยังติดเครื่องหมายคำพูดรอบ ๆ สิ่งที่คุณไม่ต้องการ ... touch \"test\"; ls -1ตัวอย่างเช่นแสดง'"test"'ใน ls ของฉัน ไม่ใช่การแยกวิเคราะห์ ... เป็นส่วนติดต่อผู้ใช้ไม่ใช่คำสั่งสคริปต์
ปีเตอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.