Misalkan saya memiliki daftar nama path file yang disimpan dalam array
filearray=("dir1/0010.pdf" "dir2/0003.pdf" "dir3/0040.pdf" )
Saya ingin mengurutkan elemen-elemen dalam array sesuai dengan nama dasar dari nama file, dalam urutan numerik
sortedfilearray=("dir2/0003.pdf" "dir1/0010.pdf" "dir3/0040.pdf")
Bagaimana saya bisa melakukan itu?
Saya hanya bisa mengurutkan bagian nama dasarnya:
basenames=()
for file in "${filearray[@]}"
do
filename=${file##*/}
basenames+=(${filename%.*})
done
sortedbasenamearr=($(printf '%s\n' "${basenames[@]}" | sort -n))
Saya sedang memikirkan
- membuat array asosiatif yang kuncinya adalah nama-dasar dan nilainya adalah nama jalur, sehingga akses ke nama jalur selalu dilakukan melalui nama-nama dasar.
- membuat array lain hanya untuk nama nama, dan berlaku
sort
untuk array nama nama.
Terima kasih.
dir1
dir2
hanya dibuat, dan mereka sebenarnya nama path yang sewenang-wenang.Jawaban:
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
atauset
atautypeset
(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 dibash
, mudah dibawa, yang paling mudah adalah dengan menggunakanperl
:Dengan
bash4.4+
, Anda bisa melakukan:Itu memberi
strcmp()
perintah seperti. Untuk pesanan yang didasarkan pada aturan pengumpulan lokal seperti di gumpalan atau keluaran darils
, tambahkan-Mlocale
argumen keperl
. Untuk pengurutan numerik (lebih seperti GNUsort -g
karena mendukung angka seperti+3
,1.2e-5
dan bukan ribuan pemisah, meskipun bukan heksadesimal), gunakan<=>
sebagai ganticmp
(dan lagi-Mlocale
untuk tanda desimal pengguna agar dihormati seperti untuksort
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:Dengan versi yang lebih lama
bash
, Anda bisa menggunakanwhile IFS= read -rd ''
perulangan alih-alihreadarray -d ''
atauperl
menampilkan keluaran jalur yang dikutip dengan benar sehingga Anda dapat meneruskannyaeval "array=($(perl...))"
.Dengan
zsh
, Anda bisa memalsukan ekspansi glob yang dapat Anda tentukan urutan pengurutan: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 GNUsort -V
, bukansort -n
yang membuat perbedaan signifikan ketika membandingkan1.4
dan1.23
(di lokal di mana.
tanda desimal) misalnya), tambahkann
kualifikasi glob.Alih-alih
oe{expression}
, Anda juga dapat menggunakan fungsi untuk menentukan urutan pengurutan seperti:atau yang lebih maju seperti:
(Jadi
a/foo2bar3.pdf
(2,3 angka) diurutkan setelahb/bar1foo3.pdf
(1,3) tetapi sebelumnyac/baz2zzz10.pdf
(2,10)) dan digunakan sebagai: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:¹ Jika
strcmp()
pengurutan berbasis-dapat diterima, dan untuk string pendek, Anda dapat mengubah string ke hex-encoding denganawk
sebelum melewatisort
dan mengubah kembali setelah pengurutan.sumber
sort
di GNU coreutils memungkinkan pemisah dan kunci bidang kustom. Anda menetapkan/
pemisah bidang dan mengurutkan berdasarkan bidang kedua untuk mengurutkan pada nama dasar, bukan seluruh jalur.printf "%s\n" "${filearray[@]}" | sort -t/ -k2
akan menghasilkansumber
sort
, bukan ekstensi GNU. Ini akan bekerja jika jalurnya semua memiliki panjang yang sama.some/long/path/0011.pdf
? Sejauh yang saya bisa lihat dari halaman manualnya,sort
tidak mengandung opsi untuk mengurutkan berdasarkan bidang terakhir.Mengurutkan dengan ekspresi gawk (didukung oleh bash 's
readarray
):Contoh array nama file yang mengandung spasi putih :
Hasil:
Mengakses satu item:
Itu mengasumsikan bahwa tidak ada jalur file berisi karakter baris baru. Perhatikan bahwa penyortiran numerik dari nilai-nilai
@val_num_asc
hanya berlaku untuk bagian numerik utama kunci (tidak ada dalam contoh ini) dengan perbandingan mundur ke leksikal (berdasarkanstrcmp()
, bukan urutan penyortiran lokal) untuk ikatan.sumber
Menyortir nama file dengan baris baru di namanya akan menyebabkan masalah pada
sort
langkah tersebut.Ini menghasilkan
/
daftar -disunting denganawk
yang berisi nama samaran di kolom pertama dan jalur lengkap sebagai kolom yang tersisa:Ini adalah apa yang diurutkan, dan
cut
digunakan untuk menghapus/
kolom yang telah direvisi pertama . Hasilnya diubah menjadibash
array baru .sumber
/some/dir/
.a/x.c++ b/x.c-- c/x.c++
akan disortir dalam urutan itu meskipun-
jenis sebelumnya+
karena-
,+
dan/
bobot utama adalah IGNORE (jadi membandingkanx.c++/a/x.c++
terhadapx.c--/b/x.c++
membandingkan pertamaxcaxc
melawanxcbxc
, dan hanya dalam kasus ikatan akan bobot lainnya (di mana-
datang sebelum+
) akan dipertimbangkan/x/
bukan/
, tapi itu tidak akan mengatasi kasus di mana di C locale pada sistem berbasis ASCII,a/foo
akan mengurutkan setelaha/foo.txt
misalnya karena/
jenis setelah.
.Karena "
dir1
dandir2
nama path arbitrer", kami tidak dapat mengandalkannya yang terdiri dari satu direktori (atau jumlah direktori yang sama). Jadi kita perlu mengkonversi slash terakhir pada nama path ke sesuatu yang tidak terjadi di tempat lain di pathname. Andaikan karakter@
tidak muncul di data Anda, Anda dapat mengurutkan berdasarkan nama nama seperti ini:sed
Perintah pertama menggantikan garis miring terakhir di setiap pathname dengan pemisah yang dipilih, yang kedua membalikkan perubahan. (Untuk kesederhanaan, saya mengasumsikan nama path dapat dikirimkan satu per baris. Jika mereka ada dalam variabel shell, ubah dulu ke format satu per baris.)sumber
cat pathnames | sed 's|\(.*\)/|\1'$'\4''|' | sort -t$'\4' -k+2nr | sed 's|'$'\4''|/|'
. (Saya baru saja meraih\4
dari tabel ascii. Rupanya "AKHIR DARI TEKS"?)\4
adalah^D
(kontrol-D). Kecuali Anda mengetiknya sendiri di terminal, itu adalah karakter kontrol biasa. Dengan kata lain, aman digunakan dengan cara ini.Solusi singkat (dan agak cepat): Dengan menambahkan indeks array ke nama file dan mengurutkannya, kita kemudian dapat membuat versi yang diurutkan berdasarkan indeks yang diurutkan.
Solusi ini hanya membutuhkan bash builtins serta
sort
biner, dan juga berfungsi dengan semua nama file yang tidak menyertakan\n
karakter baris baru .Untuk setiap file, kami mengulangi nama dasarnya dengan indeks awal ditambahkan seperti ini:
dan kemudian dikirim
sort -n
.Setelah itu kita beralih pada jalur output, ekstrak indeks lama dengan ekspansi variabel bash
${line##* }
dan masukkan elemen ini ke akhir array baru.sumber
Ini mengurutkan dengan memprioritaskan nama path file dengan nama file, mengurutkannya secara numerik, dan kemudian menghapus nama file dari bagian depan string:
Akan lebih efisien jika Anda memiliki nama file dalam daftar yang dapat dilewatkan secara langsung melalui pipa daripada sebagai array shell, karena pekerjaan yang sebenarnya dilakukan oleh
sed | sort | sed
struktur, tetapi ini sudah cukup.Saya pertama kali menemukan teknik ini ketika coding di Perl; dalam bahasa itu dikenal sebagai Schwartzian Transform .
Di Bash, transformasi seperti yang diberikan di sini dalam kode saya akan gagal jika Anda memiliki non-numerik dalam nama file. Dalam Perl itu bisa dikodekan jauh lebih aman.
sumber
$@
atau$*
dari argumen baris perintah untuk menjalankan skripUntuk nama file dengan kedalaman yang sama.
Penjelasan
Informasi diambil dari orang semacam itu.
Pencetakan array yang dihasilkan
sumber