Saya ingin bertanya:
Mengapa echo {1,2,3}diperluas ke 1 2 3 yang merupakan perilaku yang diharapkan, sementara echo [[:digit:]]kembali [[:digit:]]sementara saya berharap untuk mencetak semua digit dari 0hingga 9?
Saya ingin bertanya:
Mengapa echo {1,2,3}diperluas ke 1 2 3 yang merupakan perilaku yang diharapkan, sementara echo [[:digit:]]kembali [[:digit:]]sementara saya berharap untuk mencetak semua digit dari 0hingga 9?
Jawaban:
Karena mereka adalah dua hal yang berbeda. Ini {1,2,3}adalah contoh dari ekspansi brace . The {1,2,3}membangun diperluas oleh shell , sebelum echobahkan melihatnya. Anda dapat melihat apa yang terjadi jika Anda menggunakan set -x:
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Seperti yang Anda lihat, perintah echo {1,2,3}diperluas ke:
echo 1 2 3
Namun, [[:digit:]]adalah kelas karakter POSIX . Saat Anda memberikannya echo, shell juga memprosesnya terlebih dahulu, tapi kali ini ia diproses sebagai shell glob . ini bekerja dengan cara yang sama seperti jika Anda menjalankan echo *yang akan mencetak semua file di direktori saat ini. Tetapi [[:digit:]]apakah shell glob yang cocok dengan digit apa pun. Sekarang, dalam bash, jika shell glob tidak cocok dengan apa pun, itu akan diperluas ke dirinya sendiri:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Jika glob cocok dengan sesuatu, itu akan dicetak:
$ echo /e*c
+ echo /etc
/etc
Dalam kedua kasus, echohanya mencetak apa saja yang diperintahkan shell untuk dicetak, tetapi dalam kasus kedua, karena gumpalan cocok dengan sesuatu ( /etc) ia diminta untuk mencetak sesuatu itu.
Jadi, karena Anda tidak memiliki file atau direktori yang namanya persis terdiri dari satu digit (yang [[:digit:]]cocok dengan yang mana), gumpalan diperluas ke dirinya sendiri dan Anda mendapatkan:
$ echo [[:digit:]]
[[:digit:]]
Sekarang, coba buat file yang dipanggil 5dan jalankan perintah yang sama:
$ echo [[:digit:]]
5
Dan jika ada lebih dari satu file yang cocok:
$ touch 1 5
$ echo [[:digit:]]
1 5
Ini (semacam) didokumentasikan dalam man bashpenjelasan nullglobopsi yang mematikan perilaku ini:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Jika Anda mengatur opsi ini:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglobuntuk mendapatkan perilaku yang lebih bermanfaat mirip dengan kerang modern seperti zshatau fish.
failglob. nullglobdapat menyebabkan masalah yang tidak terduga, misalnya saat menempelkan URL yang kebetulan memiliki ?.
nullglobuntuk menunjukkan bahwa polanya ditafsirkan sebagai gumpalan oleh shell.
{1,2,3}adalah brace ekspansi , itu memperluas ke kata-kata yang tercantum tanpa memperhatikan artinya.
[...]adalah grup karakter, digunakan dalam ekspansi nama file (atau wildcard, atau glob) mirip dengan tanda bintang *dan tanda tanya ?. Ini cocok dengan setiap karakter yang terdaftar di dalam, atau karakter yang merupakan anggota dari kelompok yang disebutkan seperti [:digit:]jika mereka terdaftar. Perilaku default kebanyakan shell adalah membiarkan wildcard apa adanya jika tidak ada file yang cocok dengannya.
(Perhatikan bahwa Anda tidak dapat benar-benar mengubah wildcard / pola menjadi rangkaian string yang cocok dengan itu. Tanda bintang dapat mencocokkan string apa pun dengan panjang apa pun, jadi jika memperluas pola apa pun yang berisinya akan menghasilkan daftar string yang tak terbatas.)
Begitu:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Tetapi tetap saja:
$ bash -c 'echo {1,2,3}'
1 2 3
Keduanya diperluas oleh shell , tidak masalah jika perintah yang Anda jalankan adalah ls, atau echoatau rm. Juga perhatikan bahwa jika salah satu dari mereka dikutip, mereka tidak akan diperluas:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]] sebelum melewatinya echo, jadi echotidak pernah melihat [[:digit:]], ia hanya melihat 1 3. Anda dapat melihat ini dalam tindakan dengan menjalankan set -xyang akan mencetak perintah yang sebenarnya sedang dijalankan (jalankan set +xuntuk mematikannya lagi).
echotidak mencari file, shell tidak, sebelum menjalankan echo.
{1,2,3}(dan misalnya {1..3}adalah penjepit ekspansi . Mereka ditafsirkan oleh shell sebelum eksekusi perintah.
[[:digit:]]adalah token pencocokan pola , tetapi Anda tidak menggunakannya di lokasi dengan file apa pun yang cocok dengan pola itu. Jika Anda menggunakan kecocokan pola yang tidak memiliki kecocokan, itu berkembang dengan sendirinya:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3