Mở rộng biến tự động bên trong lệnh bash [[]]


13

Khi hội thảo một biến trong bash, bạn phải sử dụng $dấu hiệu. Tuy nhiên, có vẻ như sau đây là hoạt động tốt:

x=5
[[ x -gt 2 ]]

Bất cứ ai có thể giải thích điều này?

Chỉnh sửa: (thêm thông tin)

Ý tôi là làm thế nào và tại sao lệnh [[]] hủy bỏ biến x của tôi mà không có dấu $. Và có, nếu x = 1, câu lệnh được ước tính thành false (trả về trạng thái 1)


2
Bạn có ý nghĩa gì khi "làm việc tốt"? Và đánh giá của bạn thay đổi nếu bạn làm x=1theo [[ x -gt 2]]?
nohillside

Ý tôi là: Làm thế nào và tại sao lệnh [[]] hủy bỏ biến x của tôi mà không có dấu $. Và có, nếu x = 1, tuyên bố là sai (trạng thái trả về 1)
Khách

Câu trả lời:


9

Lý do là các -eqlực lượng đánh giá số học của các đối số.

Một toán tử số học: -eq, -gt, -lt, -ge, -le-nebên trong một [[ ]](trong ksh, zsh và bash) phương tiện để tự động mở rộng tên biến như trong ngôn ngữ c, không cần cho một hàng đầu $.

  • Để xác nhận, chúng tôi phải xem xét mã nguồn bash. Hướng dẫn không cung cấp xác nhận trực tiếp .

    Bên trong test.cviệc xử lý các toán tử số học rơi vào hàm này:

    arithcomp (s, t, op, flags)

    Ở đâu stcả hai toán hạng. Các toán hạng được trao cho chức năng này:

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

    Hàm evalexpđược định nghĩa bên trong expr.c, có tiêu đề này:

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

    Vì vậy, có, cả hai mặt của một toán tử số học (trực tiếp) rơi vào đánh giá biểu thức số học. Trực tiếp, không buts, không ifs.


Trong thực tế, với:

 $ x=3

Cả hai điều này đều thất bại:

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

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

Điều này đúng, xkhông được mở rộng và xkhông bằng một số.

Tuy nhiên:

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

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

Biến có tên xđược mở rộng (thậm chí không có $).

Điều này không xảy ra đối với một […]zsh hoặc bash (nó xảy ra trong ksh).


Điều đó giống như những gì xảy ra bên trong a $((…)):

 $ echo $(( x + 7 ))
 10

Và, xin vui lòng hiểu rằng đây là (rất) đệ quy (ngoại trừ trong dấu gạch ngang và dấu gạch ngang):

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

Một

Và khá mạo hiểm:

 $ 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")

Lỗi cú pháp có thể dễ dàng tránh được:

 $ 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

Như đã nói: vệ sinh đầu vào của bạn

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

cuối


Cả bên ngoài (cũ hơn) /usr/bin/test(không phải dựng sẵn test) và bên ngoài cũ hơn và bên ngoài exprcũng không mở rộng biểu thức chỉ các số nguyên (và rõ ràng, chỉ các số nguyên thập phân):

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

 $ expr x + 3
 expr: non-integer argument

Hấp dẫn. Không khó để nói làm thế nào điều này là có thể - là [[một từ khóa, toán tử và toán hạng được phát hiện khi lệnh được đọc và không phải sau khi mở rộng. Do đó, [[có thể điều trị -eqtheo cách thông minh hơn, nói , [. Nhưng điều tôi băn khoăn là: chúng ta có thể tìm tài liệu về bash logic ở đâu để giải thích các lệnh ghép? Nó không có vẻ khá rõ ràng đối với tôi và tôi dường như không thể tìm thấy lời giải thích thỏa đáng trong manhoặc info bash.
fra-san

Bash không tài liệu này bất cứ nơi nào tôi có thể tìm thấy. Có một loại mô tả trong man ksh93 : Các phép so sánh số học lỗi thời sau đây cũng được cho phép: exp1 -eq exp2 . Có văn bản này trong các testphần của zshbuiltins người đàn ông toán tử số học hy vọng số nguyên lập luận chứ không phải là biểu thức số học . Điều này xác nhận rằng một số đối số được coi là biểu thức số học bởi nội dung thử nghiệm trong các điều kiện không được chỉ định trong trích dẫn này. Tôi sẽ xác nhận với mã nguồn đào. Ngày
Isaac

7

Các toán hạng của so sánh số -eq, -gt, -lt, -ge, -le-neđược thực hiện như là biểu thức số học. Với một số hạn chế, chúng vẫn cần phải là các từ đơn.

Hành vi của các tên biến trong biểu thức số học được mô tả trong Shell Số học :

Biến Shell được phép dưới dạng toán hạng; mở rộng tham số được thực hiện trước khi biểu thức được ước tính. Trong một biểu thức, các biến shell cũng có thể được tham chiếu theo tên mà không cần sử dụng cú pháp mở rộng tham số. Một biến shell là null hoặc unset ước tính thành 0 khi được tham chiếu theo tên mà không sử dụng cú pháp mở rộng tham số.

và cũng:

Giá trị của một biến được đánh giá là biểu thức số học khi nó được tham chiếu

Nhưng tôi thực sự không thể tìm thấy một phần của tài liệu mà người ta nói rằng các phép so sánh số có các biểu thức số học. Nó không được mô tả trong Cấu trúc có điều kiện bên dưới [[, cũng không được mô tả trong Biểu thức điều kiện Bash .

Nhưng, bằng thí nghiệm, nó dường như hoạt động như đã nói ở trên.

Vì vậy, những thứ như thế này hoạt động:

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

điều này cũng vậy (giá trị của biến được ước tính):

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

Nhưng điều này không; nó không phải là một từ vỏ khi[[ .. ]] được phân tích cú pháp, do đó có lỗi cú pháp trong điều kiện:

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

Trong các bối cảnh số học khác, không cần biểu thức không có khoảng trắng. Bản in này999 , khi các dấu ngoặc rõ ràng phân định biểu thức số học trong chỉ mục:

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

Mặt khác, = so sánh là một kết quả khớp mẫu và không liên quan đến số học, cũng không phải việc mở rộng biến tự động được thực hiện trong ngữ cảnh số học (Cấu trúc có điều kiện):

Khi các toán tử ==!=toán tử được sử dụng, chuỗi bên phải của toán tử được coi là một mẫu và được khớp theo các quy tắc được mô tả bên dưới trong Kết hợp mẫu, như thể tùy chọn vỏ ngoài được bật. Các= nhà điều hành giống hệt ==.

Vì vậy, điều này là sai vì các chuỗi rõ ràng là khác nhau:

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

như thế này, mặc dù các giá trị số là như nhau:

[[ 6 = 06 ]] 

và ở đây cũng vậy, các chuỗi ( x6) được so sánh, chúng khác nhau:

x=6
[[ x = 6 ]]

Điều này sẽ mở rộng biến, tuy nhiên, vì vậy điều này là đúng:

x=6
[[ $x = 6 ]]

thực sự không thể tìm thấy một phần của tài liệu mà người ta nói rằng các phép so sánh số có các biểu thức số học. Các xác nhận là trong đang .
Isaac

Điều gần nhất là mô tả arg1 OP arg2nói rằng các đối số có thể là số nguyên dương hoặc âm, mà tôi đoán được cho là ngụ ý rằng chúng được coi là biểu thức số học. Một cách khó hiểu, nó cũng ngụ ý rằng họ không thể bằng không. :)
Barmar

@Barmar, ờ, đúng rồi. Nhưng điều đó cũng đúng với các phép so sánh số [, và ở đó chúng không có biểu thức số học. Thay vào đó, Bash phàn nàn về những người không có số nguyên.
ilkkachu

@ilkkachu [là một lệnh bên ngoài, nó không có quyền truy cập vào các biến shell. Nó thường được tối ưu hóa với một lệnh tích hợp, nhưng nó vẫn hoạt động như cũ.
Barmar

@Barmar, ý tôi là cụm từ "Arg1 và arg2 có thể là số nguyên dương hoặc âm". xuất hiện trong Bash Conditional Expressions , và danh sách áp dụng cho [chỉ cũng như [[. Ngay cả với [, toán hạng đến -eqvà bạn bè là / phải là số nguyên, do đó mô tả cũng được áp dụng. Lấy "phải là số nguyên" có nghĩa là "được hiểu là biểu thức số học" không áp dụng trong cả hai trường hợp. (Có lẽ ít nhất một phần là do [hoạt động như một mệnh lệnh thông thường, như bạn nói.)
ilkkachu

1

Có, quan sát của bạn là chính xác, việc mở rộng biến được thực hiện trên các biểu thức dưới dấu ngoặc kép [[ ]], vì vậy bạn không cần đặt $trước tên biến.

Điều này được nêu rõ trong bashhướng dẫn:

[[ biểu hiện ]]

(...) Chia tách từ và mở rộng tên đường dẫn không được thực hiện trên các từ giữa [[và]]; mở rộng dấu ngã, mở rộng tham số và biến, mở rộng số học, thay thế lệnh, thay thế quá trình và loại bỏ trích dẫn được thực hiện.

Lưu ý rằng đây không phải là trường hợp của phiên bản khung đơn [ ], vì [không phải là từ khóa shell (cú pháp), mà là một lệnh (trong bash nó được dựng sẵn, các shell khác có thể sử dụng bên ngoài, được lót để kiểm tra).


1
Cảm ơn vì nhắn lại. Có vẻ như điều này chỉ làm việc cho số. x = city [[$ x == city]] Điều này không hoạt động nếu không có dấu $.
Khách

3
Có vẻ như có nhiều ở đây: (x=1; [[ $x = 1 ]]; echo $?)trả về 0, (x=1; [[ x = 1 ]]; echo $?)trả về 1, tức là mở rộng tham số không được thực hiện xkhi chúng ta so sánh các chuỗi. Hành vi này trông giống như đánh giá số học được kích hoạt bởi sự mở rộng số học, tức là những gì xảy ra trong (x=1; echo $((x+1))). (Về đánh giá số học, man bashnói rằng "Trong một biểu thức, các biến shell cũng có thể được tham chiếu theo tên mà không sử dụng cú pháp mở rộng tham số).
fra-san

@ fra-san Thật vậy, vì -gttoán tử mong đợi số nên toàn bộ biểu thức được đánh giá lại như thể bên trong (()), mặt khác ==mong đợi các chuỗi nên thay vào đó chức năng khớp mẫu được kích hoạt. Tôi đã không đào sâu vào mã nguồn, nhưng nghe có vẻ hợp lý.
jimmij

[là một vỏ dựng sẵn trong bash.
Nizam Mohamed

1
@NizamMohamed Nó là một nội dung, nhưng nó vẫn không phải là một từ khóa.
Kusalananda
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.