Bài tập giống như các lệnh có trạng thái thoát trừ khi có lệnh thay thế?


10

Xem các ví dụ sau và kết quả đầu ra của chúng trong shell POSIX:

  1. false;echo $?hoặc false || echo 1:1
  2. false;foo="bar";echo $?hoặc foo="bar" && echo 0:0
  3. foo=$(false);echo $?hoặc foo=$(false) || echo 1:1
  4. foo=$(true);echo $?hoặc foo=$(true) && echo 0:0

Như được đề cập bởi câu trả lời được bình chọn cao nhất tại /programming/6834487/what-is-the-variable-in-shell-scripting :

$? được sử dụng để tìm giá trị trả về của lệnh được thực thi cuối cùng.

Đây có thể là một chút sai lệch trong trường hợp này, vì vậy hãy lấy định nghĩa POSIX cũng được trích dẫn trong một bài đăng từ chủ đề đó:

? Mở rộng đến trạng thái thoát thập phân của đường ống gần đây nhất (xem Đường ống).

Vì vậy, nó xuất hiện như thể một nhiệm vụ tự nó được tính là một lệnh (hay đúng hơn là một phần đường ống) với giá trị thoát bằng 0 nhưng áp dụng trước phía bên phải của nhiệm vụ (ví dụ: lệnh thay thế lệnh gọi trong ví dụ của tôi ở đây).

Tôi thấy hành vi này có ý nghĩa như thế nào từ quan điểm thực tế nhưng đối với tôi, việc chuyển nhượng sẽ được tính theo thứ tự đó có vẻ hơi bất thường. Có lẽ để làm rõ hơn lý do tại sao nó lạ đối với tôi, hãy giả sử nhiệm vụ là một chức năng:

ASSIGNMENT( VARIABLE, VALUE )

sau đó foo="bar"sẽ là

ASSIGNMENT( "foo", "bar" )

foo=$(false)sẽ là một cái gì đó như

ASSIGNMENT( "foo", EXECUTE( "false" ) )

điều đó có nghĩa là EXECUTEchạy trướcchỉ sau đó mới ASSIGNMENT được chạy nhưng nó vẫn là EXECUTEtrạng thái quan trọng ở đây.

Tôi có đúng trong đánh giá của tôi hay tôi hiểu sai / thiếu một cái gì đó? Đó có phải là những lý do đúng đắn để tôi xem hành vi này là "lạ"?


1
Xin lỗi, nhưng nó không rõ ràng với tôi những gì bạn thấy lạ.
Kusalananda

1
@Kusalananda Có lẽ nó giúp bạn nói với tôi rằng nó bắt đầu với tôi tự hỏi: "Tại sao false;foo="bar";echo $?luôn luôn trả về 0 khi lệnh thực sự cuối cùng đã chạy false?" Về cơ bản, các bài tập hành xử đặc biệt khi nói đến mã thoát. Mã thoát của họ luôn là 0, trừ khi không phải vì mã nào đó chạy như một phần của phía bên phải của bài tập.
phk

Câu trả lời:


10

Tình trạng thoát cho bài tập là lạ . Cách rõ ràng nhất để chuyển nhượng thất bại là nếu biến mục tiêu được đánh dấu readonly.

$ err(){ echo error ; return ${1:-1} ; }
$ PS1='$? $ '
0 $ err 42
error
42 $ A=$(err 12)
12 $ if A=$(err 9) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
E=9 ?=0
0 $ readonly A
0 $ if A=$(err 10) ; then echo wrong ; else E=$? ; echo "E=$E ?=$?" ; fi
A: is read only
1 $

Lưu ý rằng cả hai đường dẫn đúng và sai của câu lệnh if đều không được thực hiện, việc gán không dừng thực thi toàn bộ câu lệnh. bash trong chế độ POSIX và ksh93 và zsh đều sẽ hủy bỏ tập lệnh nếu chuyển nhượng thất bại.

Để trích dẫn tiêu chuẩn POSIX về điều này :

Một lệnh không có tên lệnh, nhưng một lệnh bao gồm thay thế lệnh, có trạng thái thoát của thay thế lệnh cuối cùng mà shell thực hiện.

Đây chính xác là một phần của ngữ pháp shell liên quan đến

 foo=$(err 42)

xuất phát từ một simple_command(Simple_command → cmd_prefix → ASSIGNMENT_WORD). Vì vậy, nếu một nhiệm vụ thành công thì trạng thái thoát bằng 0 trừ khi có sự thay thế lệnh, trong trường hợp đó trạng thái thoát là trạng thái của trạng thái cuối cùng. Nếu bài tập thất bại thì trạng thái thoát là khác không, nhưng bạn có thể không bắt được nó.


1
Để thêm vào câu trả lời của bạn, đây là câu trả lời từ một chủ đề khác trong đó tiêu chuẩn POSIX mới hơn về điều này được trích dẫn, về cơ bản là giống nhau: unix.stackexchange.com/a/270831/117599
phk

4

Bạn nói,

Nó xuất hiện như thể một nhiệm vụ tự nó được tính là một lệnh có giá trị thoát bằng 0, nhưng áp dụng trước bên phải của nhiệm vụ (ví dụ: lệnh thay thế lệnh gọi)

Đó không phải là một cách khủng khiếp để nhìn vào nó. Nhưng đó là một sự đơn giản hóa một chút. Tình trạng trả lại tổng thể từ

A = $ ( cmd 1 ) B = $ ( cmd 2 ) C = $ ( cmd 3 ) D = $ ( cmd 4 ) E = mc 2
là trạng thái thoát khỏi . Việc gán xảy ra sau khi gán không đặt trạng thái thoát tổng thể thành 0.cmd4E=D=

Ngoài ra, như icarus chỉ ra , các biến có thể được đặt thành chỉ đọc. Hãy xem xét các biến thể sau đây trong ví dụ của icarus:

$ err() { echo "stdout $*"; echo "stderr $*" >&2; return ${1:-1}; }
$ readonly A
$ Z=$(err 41 zebra) A=$(err 42 antelope) B=$(err 43 badger)
stderr 41 zebra
stderr 42 antelope
bash: A: readonly variable
$ echo $?
1
$ printf "%s = %s\n" Z "$Z" A "$A" B "$B"
Z = stdout 41 zebra
A =
B =
$

Mặc dù Alà chỉ đọc, bash thực hiện thay thế lệnh ở bên phải A=- và sau đó hủy bỏ lệnh vì Achỉ đọc. Điều này tiếp tục mâu thuẫn với giải thích của bạn rằng giá trị thoát của bài tập được áp dụng trước bên phải của bài tập.

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.