อักขระตัวแทน Bash star * จะสร้างรายการเรียงลำดับ (จากน้อยไปหามาก) เสมอหรือไม่


53

ฉันมีไดเรกทอรีที่เต็มไปด้วยไฟล์ที่มีชื่ออย่างlogXXที่ XX เป็นเลขฐานสองตัวอักขระสองตัวไม่มีเลขศูนย์ตัวพิมพ์ใหญ่เช่น:

log00
log01
log02
...
log0A
log0B
log0C
...
log4E
log4F
log50
...

โดยทั่วไปจะมีจำนวนไฟล์รวมน้อยกว่า 20 หรือ 30 ไฟล์ วันที่และเวลาในระบบของฉันไม่ใช่สิ่งที่สามารถพึ่งพาได้ (ระบบฝังตัวที่ไม่มีแหล่งเวลา NTP หรือ GPS ที่เชื่อถือได้) อย่างไรก็ตามชื่อไฟล์จะเพิ่มขึ้นอย่างน่าเชื่อถือดังที่แสดงไว้ด้านบน

ฉันต้องการgrepผ่านไฟล์ทั้งหมดสำหรับรายการบันทึกล่าสุดบางประเภทฉันหวังว่าจะcatรวมไฟล์เข้าด้วยกันเช่น ...

cat /tmp/logs/log* | grep 'WARNING 07 -' | tail -n1

แต่มันเกิดขึ้นกับผมว่ารุ่นที่แตกต่างกันของbashหรือshหรือzshอื่น ๆ อาจจะมีความคิดที่แตกต่างกันเกี่ยวกับวิธีการที่*จะขยายตัว

man bashหน้าไม่ได้บอกหรือไม่ว่าการขยายตัวของ*จะเป็นรายชื่อเรียงตามตัวอักษรแน่นอนขึ้นชื่อไฟล์ที่ตรงกัน ดูเหมือนว่าจะเพิ่มขึ้นทุกครั้งที่ฉันลองใช้กับระบบทั้งหมดที่ฉันมีให้ - แต่มันเป็นพฤติกรรมที่กำหนดไว้หรือเพียงแค่การนำไปใช้งานโดยเฉพาะ

กล่าวอีกนัยหนึ่งฉันสามารถพึ่งพาcat /tmp/logs/log*การเชื่อมต่อไฟล์บันทึกทั้งหมดของฉันด้วยกันตามลำดับตัวอักษรได้หรือไม่?


1
@ADDB ลำดับการจัดเรียงเริ่มต้นสำหรับsortเป็นเช่นเดียวกับที่เชลล์เมื่อมันขยายรูปแบบชื่อไฟล์กลม
Kusalananda

9
นั่นคือการตั้งชื่อไฟล์ที่แย่มาก ทำไมคุณถึงเริ่มวิ่งด้วย log (0) = - infty?
EP

14
@EP ระบบไฟล์ของเราเป็นไฮเปอร์ - ทอร์รอยด์ 7 มิติที่ซับซ้อนพร้อมหมายเลขเซอร์เรียล มันเป็นปู่กับสาขาที่คลุมเครือบางส่วนของ busybox และเราติดอยู่กับมันตอนนี้ :)
Wossname

1
คุณสามารถหลีกเลี่ยงcatกับgrep -h pattern /tmp/logs/log*การปราบปรามการ prepending ชื่อไฟล์เพื่อการแข่งขัน (อย่างน้อยกับ GNU grep ฉันไม่ได้ตรวจสอบ POSIX หรือ busybox.)
ปีเตอร์ Cordes

1
@Kusalananda คุณเคยได้ยินเรื่องการใช้งานที่ไร้ประโยชน์catนี่คือการใช้ที่ไร้ประโยชน์sort
cat

คำตอบ:


52

ในเชลล์ทั้งหมด globs จะเรียงตามค่าเริ่มต้น พวกเขาอยู่ในความ/etc/globช่วยเหลือแล้วโดยเปลือกของ Ken Thompson เพื่อขยายความมืดมนใน Unix รุ่นแรกในช่วงต้นยุค 70 (ซึ่งทำให้ชื่อของพวกมันขุ่นมัว)

สำหรับshPOSIX จะกำหนดให้เรียงลำดับตามวิธีstrcoll()ที่ใช้ลำดับการเรียงในโลแคลของผู้ใช้เหมือนกับlsบางส่วนยังคงทำผ่านstrcmp()ซึ่งขึ้นอยู่กับค่าไบต์เท่านั้น

$ dash -c 'echo *'
Log01B log-0D log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01
$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0A log0B log0C log-0D log4E log4F log50
$ ls
log  log  log00  log01  lóg01  Log01B  log02  log0A  log0B  log0C  log-0D  log4E  log4F  log50
$ ls | sort
log
log
log00
log01
lóg01
Log01B
log02
log0A
log0B
log0C
log-0D
log4E
log4F
log50

คุณอาจสังเกตเห็นข้างต้นว่าสำหรับเชลล์เหล่านั้นที่ทำการเรียงลำดับตามโลแคลที่นี่ในระบบ GNU พร้อมen_GB.UTF-8โลแคล-ในชื่อไฟล์จะถูกละเว้นสำหรับการเรียงลำดับ (อักขระเครื่องหมายวรรคตอนส่วนใหญ่) óจะถูกจัดเรียงในทางที่คาดว่าจะมากขึ้น (อย่างน้อยชาวอังกฤษ) และกรณีที่จะถูกละเว้น (ยกเว้นเมื่อมันมาถึงการตัดสินใจความสัมพันธ์)

อย่างไรก็ตามคุณจะสังเกตเห็นความไม่ลงรอยกันของบันทึกการใช้งาน นั่นเป็นเพราะลำดับการเรียงของ①และ②ไม่ได้ถูกกำหนดไว้ในที่ตั้ง GNU (ปัจจุบัน; หวังว่ามันจะได้รับการแก้ไขในบางวัน) พวกเขาเรียงลำดับเดียวกันดังนั้นคุณจะได้รับผลลัพธ์แบบสุ่ม

การเปลี่ยนโลแคลจะมีผลต่อลำดับการเรียง คุณสามารถตั้งค่าโลแคลเป็น C เพื่อstrcmp()เรียงลำดับเหมือน:

$ bash -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ bash -c 'LC_ALL=C; echo *'
Log01B log-0D log0.2 log00 log01 log02 log0A log0B log0C log4E log4F log50 log log lóg01

โปรดทราบว่าบางโลแคลสามารถทำให้เกิดความสับสนได้แม้สำหรับสตริง ASCII all-alnum ทั้งหมด เช่นเดียวกับภาษาเช็ก (ในระบบ GNU เป็นอย่างน้อย) ซึ่งchเป็นองค์ประกอบที่เรียงลำดับตามh:

$ LC_ALL=cs_CZ.UTF-8 bash -c 'echo *'
log0Ah log0Bh log0Dh log0Ch

หรือตามที่ @ninjalj ชี้ให้เห็นแม้แต่คนที่น่ากลัวในสถานที่แห่งฮังการี:

$ LC_ALL=hu_HU.UTF-8 bash -c 'echo *'
logX LOGx LOGX logZ LOGz LOGZ logY LOGY LOGy

ในzshคุณสามารถเลือกเรียงลำดับบ่น glob ตัวอย่างเช่น

echo *(om) # to sort by modification time
echo *(oL) # to sort by size
echo *(On) # for a *reverse* sort by name
echo *(o+myfunction) # sort using a user-defined function
echo *(N)  # to NOT sort
echo *(n)  # sort by name, but numerically, and so on.

การเรียงลำดับตัวเลขecho *(n)สามารถเปิดใช้งานได้ทั่วโลกด้วยnumericglobsortตัวเลือก:

$ zsh -c 'echo *'
log log log00 log01 lóg01 Log01B log02 log0.2 log0A log0B log0C log-0D log4E log4F log50
$ zsh -o numericglobsort -c 'echo *'
log log log00 lóg01 Log01B log0.2 log0A log0B log0C log01 log02 log-0D log4E log4F log50

หากคุณ (อย่างที่ฉันเคย) สับสนกับคำสั่งนั้นในตัวอย่างนั้น (โดยใช้ภาษาอังกฤษของฉัน) ดูรายละเอียดที่นี่


1
กรณี 'ch' อาจดูแปลกกว่า: บางแห่งสามารถตัดสินใจได้ว่า 'ch', 'Ch' และ 'CH' เป็นองค์ประกอบการเรียงลำดับ 1 รายการแต่ละรายการขณะที่ 'cH' เป็นองค์ประกอบการเรียงสองรายการ ดู: unicode.org/cldr/trac/ticket/889 CLDRปัจจุบันดูเหมือนจะไม่สอดคล้องกันทั้งหมด: ฮังการีปัจจุบัน ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) มีกฎเช่น&C<cs<<<Cs<<<CSขณะที่&C<cs<<<cS<<<Cs<<<CSถูกทำเครื่องหมายว่าเป็นร่างทดลองที่เสนอ เมื่อพิจารณาจากข้อมูลเก่าที่นำเข้าสู่ CLDR ดูเหมือนว่า AIX และ MS ที่เก่ากว่าจะชอบมุมมอง "ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่คือองค์ประกอบการเรียง 2 ที่แตกต่างกัน"
ninjalj

และฉันเห็นระบบที่ใช้งานไม่ได้อยู่ดี :(
Joshua

38

man page สำหรับ bash ระบุ:

การขยายชื่อพา ธ

หลังจากแยกคำเว้นแต่-fตัวเลือกที่ได้รับการตั้งค่าทุบตีสแกนแต่ละคำสำหรับตัวละคร*, และ? [หากหนึ่งในอักขระเหล่านี้ปรากฏขึ้นคำนั้นจะถือเป็นรูปแบบและแทนที่ด้วยรายการชื่อไฟล์ที่เรียงตามตัวอักษรที่ตรงกับรูปแบบ […]


1
เพิ่งพบข้อผิดพลาดที่น่าสนใจในmanการแสดงผลฉาบหรือข้อความ ... ถ้าข้อความที่ฉันค้นหาได้รับ "คำพัน" แล้วคำสั่ง / ค้นหาจะไม่พบมัน เพียงเพิ่มขนาดเทอร์มินัลของฉันและมันก็คือ :)
Wossname

2
bashคุณครอบคลุม ถ่อ OP ยังสนใจใน "zsh ฯลฯ "
Kusalananda

29

ยกเว้นว่าคุณเรียกใช้ตัวเลือกเชลล์เฉพาะเจาะจงบางอย่างในเชลล์บางตัวเอาต์พุตรับประกันว่าจะเหมือนกัน

คำสั่งซื้อถูกระบุในมาตรฐาน POSIX :

หากรูปแบบที่ตรงกับชื่อไฟล์ที่มีอยู่หรือ pathnames รูปแบบจะถูกแทนที่ด้วยชื่อไฟล์เหล่านั้นและ pathnames, เรียงตามลำดับการเรียงผลบังคับใช้ในสถานที่ปัจจุบัน หากลำดับการเรียงนี้ไม่มีการเรียงลำดับทั้งหมดของอักขระทั้งหมด (ดู XBD LC_COLLATE) ชื่อไฟล์หรือชื่อพา ธ ใด ๆ ที่เรียงเท่ากันควรเปรียบเทียบไบต์ต่อไบต์โดยใช้ลำดับการเรียงสำหรับโลแคล POSIX

ดูเพิ่มเติมที่หมวด LC_COLLATE ใน POSIX Localeซึ่งในระยะสั้นบอกว่าถ้าLC_COLLATE=Cสิ่งนั้นจะถูกจัดเรียงตามลำดับ ASCII


bashคู่มือการกล่าวถึง

LC_COLLATE

ตัวแปรนี้กำหนดลำดับการเรียงที่ใช้เมื่อเรียงลำดับผลลัพธ์ของการขยายชื่อพา ธ และกำหนดพฤติกรรมของนิพจน์ช่วงคลาสที่เท่ากันและลำดับการเรียงภายในการขยายชื่อพา ธ และการจับคู่รูปแบบ

ksh93และzshมีถ้อยคำที่คล้ายกันซึ่งทำให้ฉันเชื่อว่าพวกเขาปฏิบัติตามมาตรฐาน POSIX ในเรื่องนี้

กระสุนอื่น ๆ ที่ชอบpdkshและdashไม่พูดอะไรเกี่ยวกับการเรียงลำดับของชื่อไฟล์ที่เป็นผลมาจากการโค้งชื่อไฟล์ ฉันอยากจะเชื่อว่านี่หมายความว่าพวกเขายังคงยึดมั่นในมาตรฐานเดียวกันอย่างน้อยเมื่อใช้ภาษา POSIX จากประสบการณ์ของฉันฉันไม่ได้เจอเปลือกที่เรียงลำดับชื่อแปลก "แปลก" ของ ASCII ชื่อไฟล์


2
ดูnumericglobsortตัวเลือกzshที่จะมีผลต่อการเรียงลำดับ แม้ว่าฉันจะเปิดใช้งานแบบ per-glob echo *(n)มากกว่าที่จะเปิดใช้ตัวเลือกทั่วโลก
Stéphane Chazelas

ไม้จิ้มฟัน Bash ในโหมดเริ่มต้นไม่เป็นไปตาม Posix
fpmurphy

@ fpmurphy1 พูดมากขึ้น
Kusalananda

@Kusalananda Bash ไม่เคยได้รับการรับรองว่าเป็นคำร้องเรียน POSIX ในการรับ "การปฏิบัติตาม POSIX" ใน Bash คุณต้องเรียกใช้ Bash ด้วย--posixตัวเลือกบรรทัดคำสั่งหรือดำเนินการset -o posix
fpmurphy

@ fpmurphy1 ใช่ แต่การเรียงลำดับของการขยายชื่อตัวละครแบบวงกลมชื่อไฟล์จะไม่ได้รับผลกระทบจากposixโหมดของ Bash ดูgnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html สิ่งนี้ทำให้ฉันเชื่อ (หวังว่า) การเรียงลำดับนั้นเป็นไปตาม POSIX
Kusalananda

1

หากเป้าหมายหลักคือการเรียงลำดับไฟล์อินพุตตามอายุของไฟล์เก่าที่สุดก่อนคุณสามารถเขียนได้

(cd /tmp/logs; cat `ls -rt log*`) | grep whatever

และถ้าเกี่ยวข้องกับการหมุนและการบีบอัดล็อก:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever

4
มีการกล่าวถึงว่าการประทับเวลาของไฟล์ไม่น่าเชื่อถือ
Kusalananda

3
@ Kusalananda ถูกต้องเวลาระบบของเราโดยทั่วไปถือว่าเป็นตัวสร้างตัวเลขแบบสุ่ม :)
Wossname
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.