Berlawanan dengan ksh atau zsh, bash tidak memiliki dukungan bawaan untuk menyortir array atau daftar string yang arbitrer. Hal ini dapat mengurutkan gumpalan atau output dari alias
atau set
atau typeset
(meskipun mereka terakhir 3 tidak di lokal menyortir agar pengguna), tapi itu tidak dapat digunakan secara praktis di sini.
Tidak ada dalam toolchest POSIX yang dapat dengan mudah mengurutkan daftar string yang acak¹ ( sort
mengurutkan baris, jadi hanya pendek (LINE_MAX yang lebih pendek dari PATH_MAX) urutan karakter selain NUL dan baris baru, sedangkan jalur file adalah urutan byte kosong yang tidak kosong lainnya. dari 0).
Jadi, sementara Anda bisa menerapkan algoritma pengurutan Anda sendiri di awk
(menggunakan <
operator perbandingan string) atau bahkanbash
(menggunakan [[ < ]]
), untuk jalur sewenang-wenang di bash
, mudah dibawa, yang paling mudah adalah dengan menggunakan perl
:
Dengan bash4.4+
, Anda bisa melakukan:
readarray -td '' sorted_filearray < <(perl -MFile::Basename -l0 -e '
print for sort {basename($a) cmp basename($b)} @ARGV' -- "${filearray[@]}")
Itu memberi strcmp()
perintah seperti. Untuk pesanan yang didasarkan pada aturan pengumpulan lokal seperti di gumpalan atau keluaran dari ls
, tambahkan -Mlocale
argumen ke perl
. Untuk pengurutan numerik (lebih seperti GNU sort -g
karena mendukung angka seperti +3
, 1.2e-5
dan bukan ribuan pemisah, meskipun bukan heksadesimal), gunakan <=>
sebagai ganti cmp
(dan lagi -Mlocale
untuk tanda desimal pengguna agar dihormati seperti untuk sort
perintah).
Anda akan dibatasi oleh ukuran maksimum argumen untuk suatu perintah. Untuk menghindarinya, Anda bisa meneruskan daftar file ke perl
stdin alih-alih melalui argumen:
readarray -td '' sorted_filearray < <(
printf '%s\0' "${filearray[@]}" | perl -MFile::Basename -0le '
chomp(@files = <STDIN>);
print for sort {basename($a) cmp basename($b)} @files')
Dengan versi yang lebih lama bash
, Anda bisa menggunakan while IFS= read -rd ''
perulangan alih-alih readarray -d ''
atau perl
menampilkan keluaran jalur yang dikutip dengan benar sehingga Anda dapat meneruskannya eval "array=($(perl...))"
.
Dengan zsh
, Anda bisa memalsukan ekspansi glob yang dapat Anda tentukan urutan pengurutan:
sorted_filearray=(/(e{'reply=($filearray)'}oe{'REPLY=$REPLY:t'}))
Dengan reply=($filearray)
kami benar-benar memaksa ekspansi glob (yang awalnya hanya /
) untuk menjadi elemen array. Kemudian kita menentukan urutan pengurutan berdasarkan pada nama file.
Untuk strcmp()
pesanan seperti, perbaiki lokal ke C. Untuk jenis numerik (mirip dengan GNU sort -V
, bukan sort -n
yang membuat perbedaan signifikan ketika membandingkan 1.4
dan 1.23
(di lokal di mana .
tanda desimal) misalnya), tambahkan n
kualifikasi glob.
Alih-alih oe{expression}
, Anda juga dapat menggunakan fungsi untuk menentukan urutan pengurutan seperti:
by_tail() REPLY=$REPLY:t
atau yang lebih maju seperti:
by_numbers_in_tail() REPLY=${(j:,:)${(s:,:)${REPLY:t}//[^0-9]/,}}
(Jadi a/foo2bar3.pdf
(2,3 angka) diurutkan setelah b/bar1foo3.pdf
(1,3) tetapi sebelumnya c/baz2zzz10.pdf
(2,10)) dan digunakan sebagai:
sorted_filearray=(/(e{'reply=($filearray)'}no+by_numbers_in_tail))
Tentu saja, itu dapat diterapkan pada gumpalan nyata karena itulah tujuan utamanya. Misalnya, untuk daftar pdf
file dalam direktori apa pun, diurutkan berdasarkan nama file / ekor:
pdfs=(**/*.pdf(N.oe+by_tail))
¹ Jika strcmp()
pengurutan berbasis-dapat diterima, dan untuk string pendek, Anda dapat mengubah string ke hex-encoding dengan awk
sebelum melewati sort
dan mengubah kembali setelah pengurutan.