Apakah Bash star * wildcard selalu menghasilkan daftar (naik) yang diurutkan?

53

Saya memiliki direktori yang berisi file dengan nama-nama seperti di logXXmana XX adalah dua-karakter, nol-empuk, nomor hex huruf besar seperti:

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

Secara umum akan ada kurang dari mengatakan 20 atau 30 total file. Tanggal dan waktu pada sistem khusus saya bukanlah sesuatu yang dapat diandalkan (sistem tertanam tanpa NTP atau sumber waktu GPS). Namun nama file akan meningkat secara andal seperti yang ditunjukkan di atas.

Saya ingin grepmelalui semua file untuk entri log tunggal terbaru dari jenis tertentu, saya berharap untuk catfile bersama seperti ...

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

Namun terpikir oleh saya bahwa versi yang berbeda dari bashatau shatau zshdll mungkin memiliki ide yang berbeda tentang bagaimana *diperluas.

The man bashhalaman tidak mengatakan apakah atau tidak perluasan *akan menjadi daftar abjad pasti naik dari nama file yang cocok. Tampaknya akan naik setiap kali saya mencobanya pada semua sistem yang saya miliki - tetapi apakah itu perilaku yang DITETAPKAN atau hanya implementasi spesifik?

Dengan kata lain, bisakah saya benar-benar mengandalkan cat /tmp/logs/log*untuk menyatukan semua file log saya bersama-sama dalam urutan abjad?

Wossname
sumber
1
@ADDB Urutan sortir default untuk sortsama dengan yang untuk shell ketika itu memperluas pola globbing nama file.
Kusalananda
9
Itu praktik penamaan file yang mengerikan. Mengapa Anda mulai menjalankan dengan log (0) = - infty?
EP
14
@EP Sistem file kami adalah hyper-toroid 7 dimensi yang kompleks dengan penomoran surealis dari inode. Itu kakek dengan beberapa cabang kotak sibuk dan kami terjebak sekarang :)
Wossname
1
Anda dapat menghindari catdengan grep -h pattern /tmp/logs/log*menekan nama file yang diawali dengan pertandingan. (Setidaknya dengan GNU grep, saya tidak memeriksa POSIX atau busybox.)
Peter Cordes
1
@ Kusalananda Anda pernah mendengar tentang penggunaan yang tidak berguna cat, ini adalah penggunaan yang tidak bergunasort
cat

Jawaban:

52

Di semua shell, gumpalan diurutkan secara default. Mereka sudah oleh /etc/globpembantu yang dipanggil oleh shell Ken Thompson untuk memperluas gumpalan di versi pertama Unix di awal 70-an (dan yang memberi gumpalan nama mereka).

Sebab sh, POSIX memang mengharuskan mereka untuk disortir dengan cara strcoll(), yaitu menggunakan urutan penyortiran di lokal pengguna, seperti lsmeskipun beberapa masih melakukannya melalui strcmp(), yang didasarkan pada nilai byte saja.

$ 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

Anda mungkin memperhatikan di atas bahwa untuk shell yang melakukan pengurutan berdasarkan lokal, di sini pada sistem GNU dengan en_GB.UTF-8lokal, -dalam nama file diabaikan untuk pengurutan (kebanyakan karakter tanda baca akan). The ódiurutkan dengan cara yang lebih diharapkan (setidaknya untuk orang-orang Inggris), dan kasus diabaikan (kecuali ketika datang untuk memutuskan ikatan).

Namun, Anda akan melihat beberapa inkonsistensi untuk log① log②. Itu karena urutan penyortiran ① dan ② tidak didefinisikan di lokal GNU (saat ini; semoga akan diperbaiki suatu hari). Mereka mengurutkan sama, sehingga Anda mendapatkan hasil acak.

Mengubah lokal akan memengaruhi urutan pengurutan. Anda dapat mengatur lokal ke C untuk mendapatkan strcmp()semacam-seperti:

$ 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

Perhatikan bahwa beberapa lokal dapat menyebabkan beberapa kebingungan bahkan untuk string all-ASCII all-alnum. Seperti yang berbahasa Ceko (setidaknya pada sistem GNU) di mana chada elemen penyusun yang berurutan h:

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

Atau, seperti yang ditunjukkan oleh @ninjalj, bahkan yang lebih aneh di lokal Hongaria:

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

Di zsh, Anda dapat memilih penyortiran dengan kualifikasi glob . Misalnya:

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.

Jenis numerik echo *(n)juga dapat diaktifkan secara global dengan numericglobsortopsi:

$ 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

Jika Anda (seperti saya) bingung dengan urutan itu dalam contoh tertentu (di sini menggunakan bahasa Inggris saya), lihat di sini untuk detailnya.

Stéphane Chazelas
sumber
1
Kasus 'ch' dapat lebih aneh: beberapa lokal dapat memutuskan bahwa 'ch', 'Ch' dan 'CH' masing-masing adalah 1 elemen penyusun, sedangkan 'cH' adalah dua elemen penyusun. Lihat: unicode.org/cldr/trac/ticket/889 CLDR saat ini tampaknya tidak sepenuhnya konsisten: Hungaria saat ini ( unicode.org/cldr/trac/browser/trunk/common/collation/hu.xml ) memiliki aturan seperti &C<cs<<<Cs<<<CS, sementara &C<cs<<<cS<<<Cs<<<CSditandai sebagai konsep eksperimental yang diusulkan. Dilihat dari beberapa data lama yang diimpor ke CLDR, AIX dan MS yang lebih tua tampaknya lebih suka tampilan "huruf kecil kemudian huruf besar adalah 2 elemen susunan berbeda".
ninjalj
Dan saya telah melihat sistem di mana itu tidak berhasil. :(
Joshua
38

Halaman manual untuk bash menentukan:

Perluasan Pathname

Setelah kata membelah, kecuali -fopsi telah ditetapkan, bash memindai setiap kata untuk karakter *, ?dan [. Jika salah satu karakter ini muncul, maka kata tersebut dianggap sebagai pola, dan diganti dengan daftar nama file yang diurutkan berdasarkan abjad yang cocok dengan pola [...].

pengguna4556274
sumber
1
Baru saja menemukan bug yang menarik di manrendering dempul atau teks ... jika teks yang saya cari "dibungkus dengan kata" maka perintah / search tidak akan menemukannya. Hanya memaksimalkan terminal saya dan itu dia :)
Wossname
2
Anda tertutup bash. OP juga tertarik pada "zsh dll."
Kusalananda
29

Kecuali jika Anda memicu beberapa opsi shell yang sangat spesifik di beberapa shell, hasilnya dijamin sama.

Urutan ditentukan dalam standar POSIX :

Jika polanya cocok dengan nama file atau pathnames yang ada, polanya harus diganti dengan nama file dan pathnames tersebut, diurutkan sesuai dengan urutan susunan yang berlaku di lokal saat ini . Jika urutan penyatuan ini tidak memiliki urutan total semua karakter (lihat XBD LC_COLLATE), setiap nama file atau nama path yang disejajarkan harus lebih jauh dibandingkan byte-by-byte menggunakan urutan penyatuan untuk lokal POSIX.

Lihat juga Kategori LC_COLLATE di POSIX Locale , yang secara singkat mengatakan bahwa jika LC_COLLATE=C, maka semuanya dipesan dalam urutan ASCII.


The bashpengguna menyebutkan

LC_COLLATE

Variabel ini menentukan urutan pemeriksaan yang digunakan saat menyortir hasil ekspansi pathname, dan menentukan perilaku ekspresi rentang, kelas ekivalensi, dan menyusun urutan dalam ekspansi pathname dan pencocokan pola.

ksh93dan zshmemiliki kata-kata yang serupa, yang membuat saya percaya bahwa mereka mengikuti standar POSIX dalam hal ini.

Kerang lain, suka pdkshdan dashtidak mengatakan apa-apa tentang penyortiran nama file yang dihasilkan dari penggumpalan nama file. Saya tergoda untuk percaya bahwa ini berarti mereka masih mematuhi standar yang sama, setidaknya ketika menggunakan lokal POSIX. Dalam pengalaman saya, saya belum menemukan shell yang melakukan pemilahan nama file ASCII secara "aneh".

Kusalananda
sumber
2
Lihat numericglobsortopsi zshyang akan memengaruhi penyortiran. Meskipun saya lebih suka mengaktifkannya pada basis per-global echo *(n)daripada mengaktifkan opsi secara global.
Stéphane Chazelas
Nitpick. Bash, dalam mode default, BUKAN sesuai dengan Posix.
fpmurphy
@ fpmurphy1 Katakan lebih banyak.
Kusalananda
@ Kusalananda. Bash tidak pernah disertifikasi sebagai pengaduan POSIX. Untuk mendapatkan "Kepatuhan POSIX" di Bash, Anda harus memanggil Bash dengan --posixopsi baris perintah atau menjalankanset -o posix
fpmurphy
@ fpmurphy1 Ya, tapi penyortiran perluasan karakter globbing nama file tidak terpengaruh oleh posixmode Bash . Lihat gnu.org/software/bash/manual/html_node/Bash-POSIX-Mode.html Hal ini membuat saya percaya (berharap, lebih tepatnya) bahwa penyortirannya adalah sesuai dengan POSIX.
Kusalananda
1

Jika tujuan utamanya adalah mengurutkan file input berdasarkan umurnya, yang tertua terlebih dahulu, Anda dapat menulis

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

Dan jika log diputar dan dikompresi juga terlibat:

(cd /tmp/logs; zcat -f `ls -rt log*`) | grep whatever
sultansofswing
sumber
4
Disebutkan bahwa cap waktu pada file tidak dapat dipercaya.
Kusalananda
3
@ Kusalananda, itu benar, waktu sistem kami umumnya dianggap sebagai generator nomor acak :)
Wossname