Saya berada di direktori di mana saya memiliki dua file teks:
$ touch test1.txt
$ touch test2.txt
Ketika saya mencoba membuat daftar file (dengan Bash) menggunakan beberapa pola kerjanya:
$ ls test?.txt
test1.txt test2.txt
$ ls test{1,2}.txt
test1.txt test2.txt
Namun, ketika sebuah pola dihasilkan oleh perintah yang disertakan $()
, hanya satu pola yang berfungsi:
$ ls $(echo 'test?.txt')
test1.txt test2.txt
$ ls $(echo 'test{1,2}.txt')
ls: cannot access test{1,2}.txt: No such file or directory
Apa yang terjadi di sini? Mengapa polanya {1,2}
tidak bekerja?
bash
bash-expansion
Herosław Miraszewski
sumber
sumber
?
itu juga dikutip, dan akan diperluas setelah$(...)
menggantinya, tetapi ekspansi brace tidak.$
-expansions:zsh -o globsubst -c 'a=/e*; b={/b*,/v*}; echo $a; echo $b'
.Jawaban:
Ini kombinasi dari dua hal. Pertama, ekspansi brace bukan pola yang cocok dengan nama file: ini adalah pengganti tekstual murni - lihat Apa perbedaan antara `a [bc] d` (kurung) dan` a {b, c} d` (kurung kurawal)? . Kedua, ketika Anda menggunakan hasil substitusi perintah di luar tanda kutip ganda (
ls $(…)
), yang terjadi hanya pencocokan pola (dan pemisahan kata: operator "split + glob"), bukan penguraian ulang yang lengkap.Dengan
ls $(echo 'test?.txt')
, perintahecho 'test?.txt'
menampilkan stringtest?.txt
(dengan baris baru akhir). Substitusi perintah menghasilkan stringtest?.txt
(tanpa baris baru final, karena subtitusi perintah membuntuti baris baru). Substitusi yang tidak dikutip ini mengalami pemisahan kata, menghasilkan daftar yang terdiri dari string tunggaltest?.txt
karena tidak ada karakter spasi (lebih tepatnya, tidak ada karakter$IFS
di dalamnya) di dalamnya. Setiap elemen dari daftar satu elemen ini kemudian mengalami ekspansi wildcard bersyarat, dan karena ada karakter wildcard?
dalam string, ekspansi wildcard tersebut benar-benar terjadi. Karena polatest?.txt
cocok dengan setidaknya satu nama file, elemen daftartest?.txt
diganti dengan daftar nama file yang cocok dengan pola, menghasilkan daftar dua elemen yang mengandungtest1.txt
dantest2.txt
. Akhirnyals
disebut dengan dua argumentest1
dantest2
.Dengan
ls $(echo 'test{1,2}')
, perintahecho 'test{1,2}'
menampilkan stringtest{1,2}
(dengan baris baru akhir). Substitusi perintah menghasilkan stringtest{1,2}
. Substitusi yang tidak dikutip ini mengalami pemisahan kata, menghasilkan daftar yang terdiri dari string tunggaltest{1,2}
. Setiap elemen dari daftar satu elemen ini kemudian mengalami ekspansi wildcard bersyarat, yang tidak melakukan apa-apa (elemen dibiarkan apa adanya) karena tidak ada karakter wildcard dalam string. Demikianls
disebut dengan argumen tunggaltest{1,2}
.Sebagai perbandingan, inilah yang terjadi
ls $(echo test{1,2})
. Perintahecho test{1,2}
menampilkan stringtest1 test2
(dengan baris terakhir akhir). Substitusi perintah menghasilkan stringtest1 test2
(tanpa baris akhir akhir). Substitusi yang tidak dikutip ini mengalami pemisahan kata, menghasilkan dua stringtest1
dantest2
. Kemudian, karena tidak satu pun dari string berisi karakter wildcard, mereka dibiarkan sendiri, sehinggals
disebut dengan dua argumentest1
dantest2
.sumber
.txt
pada penjelasan kedua.Ekspansi brace tidak akan terjadi setelah penggantian perintah. Anda dapat menggunakan eval untuk memaksa putaran ekspansi lainnya:
Hasilnya adalah:
sumber
Masalah itu sangat spesifik
bash
, dan itu karena mereka memutuskanbash
untuk memisahkan ekspansi brace dari ekspansi nama file (globbing), dan untuk melakukannya terlebih dahulu, sebelum semua ekspansi lainnya.Dari halaman
bash
manual:Dalam contoh Anda,
bash
hanya akan melihat kawat gigi Anda setelah melakukan penggantian perintah (yang$(echo ...)
), ketika sudah terlambat.Ini berbeda dari semua shell lainnya, yang melakukan ekspansi brace tepat sebelum (dan beberapa bahkan sebagai bagian dari) ekspansi pathname (globbing). Itu termasuk tetapi tidak terbatas pada
csh
tempat brace-ekspansi pertama kali ditemukan.Contoh terakhir adalah di sama
csh
,zsh
,ksh93
,mksh
ataufish
.Juga, perhatikan bahwa penjepit ekspansi sebagai bagian dari globbing juga tersedia melalui
glob(3)
fungsi pustaka (setidaknya di Linux dan semua BSD), dan dalam implementasi independen lainnya (misalnya dalam perl:)perl -le 'print join " ", <test{1,2}.txt>'
.Mengapa hal itu dilakukan secara berbeda
bash
mungkin memiliki cerita di baliknya, tetapi FWIW saya tidak dapat menemukan penjelasan logis, dan saya menemukan semua rasionalisasi post-hoc tidak meyakinkan.sumber
perl
dulu digunakan untukcsh
memperluas gumpalan, jadi tidak mengherankan bahwa ia masih mengenali operator globbing yang sama dengancsh
Silakan coba :::
ls $ (echo test {1,2} \. txt)
Dengan BackSlash. Ini berfungsi sekarang. Hapus juga seperti kata poster sebelumnya, kutipan. Dot bukan untuk pola yang cocok, tetapi harus diambil secara harfiah sebagai Periode di sini.
sumber
{1,2}
berperilaku seperti itu? Pertanyaannya tidak bertanya, "Bagaimana saya bisa mendapatkan perintah menggunakan{1,2}
untuk berperilaku cara kerja perintah?
?" Anda menjawab pertanyaan yang salah. (2) Backslash tidak ada hubungannya dengan itu. Perintah Anda berfungsi sebagaimana mestinya karena Anda telah menghapus tanda kutip yang ada di perintah dalam pertanyaan.Ini berfungsi jika Anda menghapus tanda kutip
sumber
?
).