Perintah ekspansi variabel otomatis di dalam bash [[]]

13

Saat mendereferensi variabel dalam bash, Anda harus menggunakan $tanda. Namun demikian, tampaknya yang berikut ini berfungsi dengan baik:

x=5
[[ x -gt 2 ]]

Adakah yang bisa menjelaskan ini?

Edit: (info lebih lanjut)

Yang saya maksud adalah bagaimana dan mengapa perintah [[]] men-dereferensi variabel saya x tanpa tanda $. Dan ya, jika x = 1, pernyataan dievaluasi ke false (status pengembalian 1)

Tamu
sumber
2
Apa yang Anda maksud dengan "bekerja dengan baik"? Dan apakah penilaian Anda berubah jika Anda x=1diikuti [[ x -gt 2]]?
nohillside
Maksud saya: Bagaimana dan mengapa perintah [[]] mendereferensi variabel x saya tanpa tanda $. Dan ya, jika x = 1, pernyataan itu salah (status pengembalian 1)
Tamu

Jawaban:

9

Alasannya adalah bahwa -eqkekuatan evaluasi aritmatika argumen.

Operator aritmatika: -eq, -gt, -lt, -ge, -ledan -nedi dalam [[ ]](di ksh, zsh dan bash) alat untuk secara otomatis memperluas nama variabel seperti dalam bahasa c, tidak perlu untuk memimpin $.

  • Untuk konfirmasi, kita harus melihat kode sumber bash. Manual tidak menawarkan konfirmasi langsung .

    Di test.cdalam pemrosesan operator aritmatika termasuk dalam fungsi ini:

    arithcomp (s, t, op, flags)

    Di mana sdan tkeduanya operan. Operan diserahkan ke fungsi ini:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    Fungsi evalexpdidefinisikan di dalam expr.c, yang memiliki tajuk ini:

    /* expr.c -- arithmetic expression evaluation. */

    Jadi, ya, kedua sisi operator aritmatika jatuh (langsung) ke dalam evaluasi ekspresi aritmatika. Secara langsung, tidak ada tetapi, tidak ada.


Dalam praktiknya, dengan:

 $ x=3

Keduanya gagal:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

Yang benar, xtidak diperluas dan xtidak sama dengan angka.

Namun:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

Variabel bernama xakan diperluas (bahkan tanpa $).

Ini tidak terjadi untuk […]di zsh atau bash (tidak di ksh).


Itu sama dengan apa yang terjadi di dalam $((…)):

 $ echo $(( x + 7 ))
 10

Dan, harap dipahami bahwa ini (sangat) rekursif (kecuali dalam tanda hubung dan yash):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

Dan cukup berisiko:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

Kesalahan sintaksis dapat dengan mudah dihindari:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

Seperti kata pepatah: membersihkan input Anda

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

akhir 😮


Baik eksternal (lama) /usr/bin/test(bukan builtin test) dan yang masih lebih tua dan juga eksternal exprtidak memperluas ekspresi hanya bilangan bulat (dan tampaknya, hanya bilangan bulat desimal):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument
Ishak
sumber
Menarik. Tidak sulit untuk mengetahui bagaimana hal ini mungkin - menjadi [[kata kunci, operator dan operan terdeteksi ketika perintah dibaca dan bukan setelah ekspansi. Dengan demikian [[dapat memperlakukan -eqdengan cara yang lebih pintar daripada, katakanlah [,. Tapi yang saya ingin tahu adalah: di mana kita dapat menemukan dokumentasi tentang penggunaan logika bash untuk menafsirkan perintah majemuk? Tidak terlihat jelas bagi saya dan saya tampaknya tidak dapat menemukan penjelasan yang memuaskan di manatau info bash.
fra-san
Bash tidak mendokumentasikan ini di mana pun saya dapat temukan. Ada semacam deskripsi dalam man ksh93 : Perbandingan aritmatika usang berikut juga diizinkan: exp1 -eq exp2 . Ada teks ini di dalam testbagian manusia zshbuiltins operator aritmatika berharap bilangan bulat argumen daripada ekspresi aritmatika . Yang menegaskan bahwa beberapa argumen diperlakukan sebagai ekspresi aritmatika oleh tes yang dibangun di bawah kondisi yang tidak ditentukan dalam kutipan ini. Saya akan mengkonfirmasi dengan kode sumber .... ...
Isaac
7

Operan dari perbandingan numerik -eq, -gt, -lt, -ge, -ledan-ne diambil sebagai ekspresi aritmatika. Dengan beberapa batasan, mereka masih perlu kata-kata shell tunggal.

Perilaku nama variabel dalam ekspresi aritmatika dijelaskan dalam Aritmatika Shell :

Variabel Shell diizinkan sebagai operan; ekspansi parameter dilakukan sebelum ekspresi dievaluasi. Di dalam ekspresi, variabel shell juga dapat dirujuk oleh nama tanpa menggunakan sintaks ekspansi parameter. Variabel shell yang nol atau tidak disetel bernilai 0 ketika direferensikan oleh nama tanpa menggunakan sintaks ekspansi parameter.

dan juga:

Nilai variabel dievaluasi sebagai ekspresi aritmatika ketika direferensikan

Tetapi saya tidak dapat menemukan bagian dari dokumentasi di mana dikatakan bahwa perbandingan numerik mengambil ekspresi aritmatika. Itu tidak dijelaskan dalam Konstruk Kondisional di bawah [[, juga tidak dijelaskan dalam Ekspresi Bersyarat Bash .

Tetapi, dengan eksperimen, tampaknya berfungsi seperti yang dikatakan di atas.

Jadi, hal-hal seperti ini berfungsi:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

ini juga (nilai variabel dievaluasi):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

Tetapi ini tidak; itu bukan kata shell tunggal ketika [[ .. ]]diuraikan, jadi ada kesalahan sintaksis dalam kondisi:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

Dalam konteks aritmatika lainnya, ekspresi tidak perlu tanpa spasi putih. Ini mencetak 999, karena tanda kurung secara jelas membatasi ekspresi aritmatika dalam indeks:

a[6]=999; echo ${a[1 + 2 + 3]}

Di sisi lain, =perbandingannya adalah kecocokan pola , dan tidak melibatkan aritmatika, maupun ekspansi variabel otomatis yang dilakukan dalam konteks aritmatika (Konstruk Kondisional):

Kapan ==dan!= operator digunakan, string di sebelah kanan operator dianggap sebagai pola dan dicocokkan sesuai dengan aturan yang dijelaskan di bawah dalam Pencocokan Pola, seolah-olah opsi cangkang extglob diaktifkan. The =operator adalah identik dengan ==.

Jadi ini salah karena senarnya jelas berbeda:

[[ "1 + 2 + 3" = 6 ]] 

seperti ini, meskipun nilai numeriknya sama:

[[ 6 = 06 ]] 

dan di sini juga, senar ( xdan6 ) dibandingkan, mereka berbeda:

x=6
[[ x = 6 ]]

Ini akan memperluas variabel, jadi ini benar:

x=6
[[ $x = 6 ]]
ilkkachu
sumber
tidak dapat benar-benar menemukan bagian dari dokumentasi di mana dikatakan bahwa perbandingan numerik mengambil ekspresi aritmatika. The konfirmasi dalam kode .
Isaac
Hal terdekat adalah bahwa deskripsi arg1 OP arg2mengatakan bahwa argumen mungkin bilangan bulat positif atau negatif, yang saya kira seharusnya menyiratkan bahwa mereka diperlakukan sebagai ekspresi aritmatika. Yang membingungkan, itu juga menyiratkan bahwa mereka tidak boleh nol. :)
Barmar
@Barmar, ehh, benar. Tapi itu berlaku untuk perbandingan numerik [juga, dan tidak ada ekspresi aritmatika. Sebaliknya, Bash mengeluh tentang non-bilangan bulat.
ilkkachu
@ilkkachu [adalah perintah eksternal, ia tidak memiliki akses ke variabel shell. Ini sering dioptimalkan dengan perintah bawaan, tetapi masih berlaku sama.
Barmar
@Barmar, yang saya maksudkan adalah kalimat "Arg1 dan arg2 mungkin bilangan bulat positif atau negatif." muncul di Ekspresi Bersyarat Bash , dan daftar itu berlaku [juga [[. Bahkan dengan [, operan ke -eqdan teman harus / harus bilangan bulat, sehingga deskripsi juga berlaku. Mengambil "harus bilangan bulat" berarti "ditafsirkan sebagai ekspresi aritmatika" tidak berlaku dalam kedua kasus. (Mungkin setidaknya sebagian karena [bertindak seperti perintah biasa, seperti yang Anda katakan.)
ilkkachu
1

Ya, pengamatan Anda benar, ekspansi variabel dilakukan pada ekspresi di bawah tanda kurung ganda [[ ]], jadi Anda tidak perlu meletakkan $di depan nama variabel.

Ini secara eksplisit dinyatakan dalam bashmanual:

[[ekspresi]]

(...) Pemisahan kata dan perluasan pathname tidak dilakukan pada kata-kata antara [[dan]]; ekspansi tilde, ekspansi parameter dan variabel, ekspansi aritmatika, penggantian perintah, penggantian proses, dan penghapusan kutipan dilakukan.

Perhatikan bahwa ini bukan kasus versi braket tunggal [ ], karena [bukan kata kunci shell (sintaks), melainkan sebuah perintah (dalam bash itu dibangun di dalam, shell lain dapat menggunakan eksternal, dilapisi untuk menguji).

jimmij
sumber
1
Terima kasih untuk balasannya. Tampaknya ini hanya berfungsi untuk angka. x = city [[$ x == city]] Ini tidak berfungsi tanpa tanda $.
Tamu
3
Sepertinya ada lebih banyak di sini: (x=1; [[ $x = 1 ]]; echo $?)pengembalian 0, (x=1; [[ x = 1 ]]; echo $?)pengembalian 1, yaitu ekspansi parameter tidak dilakukan xketika kita membandingkan string. Perilaku ini seperti evaluasi aritmatika yang dipicu oleh ekspansi aritmatika, yaitu apa yang terjadi di (x=1; echo $((x+1))). (Tentang evaluasi aritmatika, man bashnyatakan bahwa "Dalam sebuah ekspresi, variabel shell juga dapat dirujuk dengan nama tanpa menggunakan sintaksis ekspansi parameter).
fra-san
@ fra-san Memang, karena -gtoperator mengharapkan angka sehingga seluruh ekspresi dievaluasi kembali seolah-olah di dalam (()), di sisi lain ==mengharapkan string sehingga alih-alih fungsi pencocokan pola dipicu. Saya tidak menggali ke dalam kode sumber, tetapi kedengarannya masuk akal.
jimmij
[adalah shell builtin dalam bash.
Nizam Mohamed
1
@NizamMohamed Ini adalah builtin, tapi itu masih bukan kata kunci.
Kusalananda