Kerang seperti Bash dan Zsh memperluas wildcard menjadi argumen, sebanyak mungkin argumen yang cocok dengan polanya:
$ echo *.txt
1.txt 2.txt 3.txt
Tetapi bagaimana jika saya hanya ingin pertandingan pertama dikembalikan, tidak semua pertandingan?
$ echo *.txt
1.txt
Saya tidak keberatan dengan solusi khusus shell, tetapi saya ingin solusi yang bekerja dengan spasi putih dalam nama file.
Jawaban:
Salah satu cara yang kuat dalam bash adalah memperluas ke array, dan output elemen pertama saja:
(Anda bahkan bisa saja
echo $files
, indeks yang hilang diperlakukan sebagai [0].)Ini dengan aman menangani spasi / tab / baris baru dan karakter metakarakter lainnya saat memperluas nama file. Perhatikan bahwa pengaturan lokal yang berlaku dapat mengubah apa yang "pertama" itu.
Anda juga dapat melakukan ini secara interaktif dengan fungsi penyelesaian bash :
Ini mengikat
_echo
fungsi untuk melengkapi argumen keecho
perintah (menimpa penyelesaian normal). "*" Tambahan ditambahkan dalam kode di atas, Anda bisa menekan tab pada nama file parsial dan mudah-mudahan hal yang benar akan terjadi.Kode sedikit berbelit-belit, daripada disetel atau diasumsikan
nullglob
(shopt -s nullglob
) kita periksacompgen -G
dapat memperluas gumpalan ke beberapa kecocokan, kemudian kita memperluas dengan aman ke dalam array, dan akhirnya mengatur KOMPREPLI sehingga kuotasi kuat.Anda bisa melakukan ini sebagian (secara terprogram memperluas gumpalan) dengan bash
compgen -G
, tetapi tidak kuat karena outputnya tidak dikutip ke stdout.Seperti biasa, penyelesaian agak penuh, hal ini merusak penyelesaian hal-hal lain, termasuk variabel lingkungan (lihat
_bash_def_completion()
fungsi di sini untuk detail meniru perilaku default).Anda juga bisa menggunakan
compgen
luar fungsi penyelesaian:Satu hal yang perlu diperhatikan adalah bahwa "~" bukan glob, itu ditangani oleh bash dalam tahap ekspansi yang terpisah, seperti $ variable dan ekspansi lainnya.
compgen -G
hanya melakukan globbing nama file, tetapicompgen -W
memberi Anda semua perluasan default bash, meskipun mungkin terlalu banyak ekspansi (termasuk``
dan$()
). Tidak seperti-G
itu-W
adalah aman dikutip (saya tidak bisa menjelaskan perbedaan tersebut). Karena tujuannya-W
adalah untuk memperluas token, ini berarti ia akan memperluas "a" ke "a" bahkan jika tidak ada file seperti itu, jadi itu mungkin tidak ideal.Ini lebih mudah dipahami, tetapi mungkin memiliki efek samping yang tidak diinginkan:
Kemudian:
touch $'curious \n filename'
echo curious*
tabPerhatikan penggunaan
printf %q
untuk mengutip nilai-nilai secara aman.Salah satu opsi terakhir adalah menggunakan output yang dibatasi 0 dengan utilitas GNU (lihat bash FAQ ):
Opsi ini memberi Anda sedikit lebih banyak kontrol atas urutan penyortiran (urutan ketika memperluas gumpalan akan tunduk pada lokal Anda /
LC_COLLATE
dan mungkin atau mungkin tidak melipat kasing), tetapi sebaliknya palu yang agak besar untuk masalah kecil ;-)sumber
Di zsh, gunakan
[1]
kualifikasi glob . Perhatikan bahwa meskipun case khusus ini mengembalikan paling banyak satu pertandingan, itu masih daftar, dan gumpalan tidak diperluas dalam konteks yang mengharapkan satu kata seperti tugas (penugasan array array).Dalam ksh atau bash, Anda dapat memasukkan seluruh daftar kecocokan dalam sebuah array dan menggunakan elemen pertama.
Dalam shell apa pun, Anda dapat mengatur parameter posisi dan menggunakan yang pertama.
Ini mengacaukan parameter posisi. Jika Anda tidak menginginkannya, Anda dapat menggunakan subkulit.
Anda juga dapat menggunakan fungsi, yang memiliki set parameter posisinya sendiri.
sumber
*.txt([1,n])
Mencoba:
Catatan bahwa ekspansi nama file diurutkan sesuai dengan urutan pengumpatan yang berlaku di lokal saat ini.
sumber
Solusi sederhana:
Atau gunakan
printf
jika Anda suka.sumber
Saya hanya menemukan pertanyaan lama ini sambil bertanya-tanya hal yang sama. Saya berakhir dengan ini:
echo $(ls *.txt | head -n1)
Anda dapat, tentu saja, ganti
head
dengantail
dan-n1
dengan nomor lainnya.Di atas tidak akan berfungsi jika Anda bekerja dengan file yang memiliki baris baru dalam namanya. Untuk bekerja dengan baris baru, Anda dapat menggunakan salah satu dari ini:
ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g'
(Tidak berfungsi pada BSD)ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
echo -e "$(ls -b *.txt | head -n1)"
(Bekerja dengan karakter khusus apa saja)sumber
Kasus penggunaan yang sering saya temui adalah mendefinisikan direktori atas / bawah setelah ekspansi glob (mis. Direktori yang penuh dengan SDK versi atau alat bantu bangunan). Dalam situasi ini saya biasanya ingin menyimpan nama direktori itu ke variabel untuk digunakan di beberapa tempat dalam skrip shell.
Perintah ini biasanya melakukannya untuk saya:
Penafian: Perluasan global tidak akan menyortir folder Anda dengan semver; Anda sudah diperingatkan. Ini bagus jika Anda memiliki
Dockerfile
yang hanya satu versi direktori, tetapi versi direktori dapat bervariasi dari gambar ke gambar 🤷sumber
mkdir "$(echo one; echo two)"
dan lihat apa yang saya maksud.tail
?dirname
hanya membutuhkan satu nama path, jadi Anda tidak bisa mengandalkan itu bekerja pada beberapa nama path kecuali Anda tahu implementasi khusus Anda mendukungnya.