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 0
hingga 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 0
hingga 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 echo
bahkan 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, echo
hanya 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 5
dan 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 bash
penjelasan nullglob
opsi 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 failglob
untuk mendapatkan perilaku yang lebih bermanfaat mirip dengan kerang modern seperti zsh
atau fish
.
failglob
. nullglob
dapat menyebabkan masalah yang tidak terduga, misalnya saat menempelkan URL yang kebetulan memiliki ?
.
nullglob
untuk 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 echo
atau 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 echo
tidak pernah melihat [[:digit:]]
, ia hanya melihat 1 3
. Anda dapat melihat ini dalam tindakan dengan menjalankan set -x
yang akan mencetak perintah yang sebenarnya sedang dijalankan (jalankan set +x
untuk mematikannya lagi).
echo
tidak 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