Nó được ghi lại (cho POSIX) trong Phần 2.9.1 Các lệnh đơn giản
của Thông số kỹ thuật cơ sở nhóm mở. Có một bức tường văn bản ở đó; Tôi hướng sự chú ý của bạn đến đoạn cuối:
Nếu có tên lệnh, việc thực thi sẽ tiếp tục như được mô tả trong Tìm kiếm và Thực thi lệnh . Nếu không có tên lệnh, nhưng lệnh chứa thay thế lệnh, lệnh sẽ hoàn thành với trạng thái thoát của thay thế lệnh cuối cùng được thực hiện. Nếu không, lệnh sẽ hoàn thành với trạng thái thoát không.
Ví dụ,
Command Exit Status
$ FOO=BAR 0 (but see also the note from icarus, below)
$ FOO=$(bar) Exit status from "bar"
$ FOO=$(bar) baz Exit status from "baz"
$ foo $(bar) Exit status from "foo"
Đây là cách bash hoạt động, quá. Nhưng cũng thấy phần đầu không đơn giản như vậy.
phk , trong câu hỏi của mình 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ế? , gợ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ó. Đề án thô để xác định tình trạng trở lại của một lệnh đơn giản (một không chứa ;
, &
, |
, &&
hoặc ||
) là:
- Quét dòng từ trái sang phải cho đến khi bạn đạt đến cuối hoặc một từ lệnh (thường là tên chương trình).
- Nếu bạn thấy một phép gán biến, trạng thái trả về cho dòng chỉ có thể là 0.
- Nếu bạn thấy một sự thay thế lệnh - tức là,
$(…)
- hãy lấy trạng thái thoát khỏi lệnh đó.
- Nếu bạn đạt được một lệnh thực tế (không phải thay thế lệnh), hãy lấy trạng thái thoát khỏi lệnh đó.
Trạng thái trả về cho dòng là số cuối cùng bạn gặp phải.
Thay thế lệnh làm đối số cho lệnh, ví dụ, foo $(bar)
không được tính; bạn có được trạng thái thoát khỏi foo
. Để diễn giải ký hiệu của phk , hành vi ở đây là
temporary_variable = EXECUTE( "bar" )
overall_exit_status = EXECUTE( "foo", temporary_variable )
Nhưng đây là một sự đơn giản hóa nhẹ. 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.
cmd4
E=
D=
icarus , trong câu trả lời cho câu hỏi của phk , đưa ra một điểm quan trọng: các biến có thể được đặt thành chỉ đọc. Đoạn thứ ba đến cuối cùng trong Phần 2.9.1 của tiêu chuẩn POSIX cho biết,
Nếu bất kỳ phép gán biến nào cố gán giá trị cho biến mà thuộc tính readonly được đặt trong môi trường shell hiện tại (bất kể việc gán có được thực hiện trong môi trường đó không), sẽ xảy ra lỗi gán biến. Xem Hậu quả của lỗi Shell để biết hậu quả của những lỗi này.
vì vậy nếu bạn nói
readonly A
C=Garfield A=Felix T=Tigger
tình trạng trở lại là 1. Nó không quan trọng nếu các dây Garfield
, Felix
và / hoặc Tigger
được thay thế bằng thay thế lệnh (s) - nhưng xem ghi chú bên dưới.
Mục 2.8.1 Hậu quả của lỗi Shell có một loạt văn bản khác, và một bảng và kết thúc bằng
Trong tất cả các trường hợp được hiển thị trong bảng trong đó trình bao tương tác được yêu cầu không thoát, trình bao sẽ không thực hiện bất kỳ xử lý nào nữa của lệnh trong đó xảy ra lỗi.
Một số chi tiết có ý nghĩa; một số không:
- Việc
A=
gán đôi khi hủy bỏ dòng lệnh, vì câu cuối cùng đó dường như chỉ định. Trong ví dụ trên, C
được đặt thành Garfield
, nhưng T
không được đặt (và, tất nhiên, cũng không A
).
- Tương tự,
thực thi
nhưng không . Nhưng, trong các phiên bản của tôi về bash (trong đó bao gồm 4.1.x và 4.3.x), nó không thực hiện . (Ngẫu nhiên, điều này càng thúc đẩy sự giải thích của phk rằng giá trị thoát của bài tập được áp dụng trước phần bên phải của bài tập.)
C=$(cmd1) A=$(cmd2) T=$(cmd3)
cmd1
cmd3
cmd2
Nhưng đây là một bất ngờ:
Trong các phiên bản bash của tôi,
chỉ đọc A
C = cái gì đó A = cái gì đó T = cái gì đó cmd 0
không thực hiện. Đặc biệt,cmd0
C = $ ( cmd 1 ) A = $ ( cmd 2 ) T = $ ( cmd 3 ) cmd 0
thực thi
và , nhưng không . (Lưu ý rằng điều này ngược lại với hành vi của nó khi không có lệnh.) Và nó đặt (cũng như ) trong môi trường của . Tôi tự hỏi liệu đây có phải là một lỗi trong bash.
cmd1
cmd3
cmd2
T
C
cmd0
Không đơn giản lắm:
Đoạn đầu tiên của câu trả lời này đề cập đến các lệnh đơn giản.
Các đặc điểm kỹ thuật nói,
Một lệnh đơn giản của người Viking là một chuỗi các phép gán và chuyển hướng tùy chọn, trong bất kỳ chuỗi nào, tùy ý theo sau là các từ và chuyển hướng, được chấm dứt bởi một toán tử điều khiển.
Đây là những câu như những câu trong khối ví dụ đầu tiên của tôi:
$ FOO=BAR
$ FOO=$(bar)
$ FOO=$(bar) baz
$ foo $(bar)
ba trong số đó bao gồm các bài tập biến và ba trong số đó bao gồm các lệnh thay thế.
Nhưng một số bài tập biến không đơn giản như vậy.
bash (1) nói rằng
Câu lệnh gán cũng có thể xuất hiện như các đối số vào alias
, declare
, typeset
, export
, readonly
, và local
được xây dựng trong các lệnh ( tuyên bố lệnh).
Đối với export
, đặc tả POSIX nói,
TÌNH TRẠNG EXIT
0Tất cả các toán hạng tên đã được xuất thành công.
> 0Ít nhất một tên không thể được xuất hoặc -p
tùy chọn đã được chỉ định và đã xảy ra lỗi.
Và POSIX không hỗ trợ local
, nhưng bash (1) nói,
Đó là một lỗi để sử dụng local
khi không nằm trong một chức năng. Trạng thái trả về là 0 trừ khi local
được sử dụng bên ngoài hàm, tên không hợp lệ được cung cấp hoặc tên là biến chỉ đọc.
Đọc giữa các dòng, chúng ta có thể thấy các lệnh khai báo như
export FOO=$(bar)
và
local FOO=$(bar)
giống hơn
foo $(bar)
trong chừng mực họ bỏ qua những trạng thái thoát khỏi bar
và cung cấp cho bạn một trạng thái thoát dựa trên lệnh chính ( export
, local
hoặc foo
). Vì vậy, chúng tôi có sự kỳ lạ như
Command Exit Status
$ FOO=$(bar) Exit status from "bar"
(unless FOO is readonly)
$ export FOO=$(bar) 0 (unless FOO is readonly,
or other error from “export”)
$ local FOO=$(bar) 0 (unless FOO is readonly,
statement is not in a function,
or other error from “local”)
mà chúng ta có thể chứng minh với
$ export FRIDAY=$(date -d tomorrow)
$ echo "FRIDAY = $FRIDAY, status = $?"
FRIDAY = Fri, May 04, 2018 8:58:30 PM, status = 0
$ export SATURDAY=$(date -d "day after tomorrow")
date: invalid date ‘day after tomorrow’
$ echo "SATURDAY = $SATURDAY, status = $?"
SATURDAY = , status = 0
và
myfunc() {
local x=$(echo "Foo"; true); echo "x = $x -> $?"
local y=$(echo "Bar"; false); echo "y = $y -> $?"
echo -n "BUT! "
local z; z=$(echo "Baz"; false); echo "z = $z -> $?"
}
$ myfunc
x = Foo -> 0
y = Bar -> 0
BUT! z = Baz -> 1
May mắn thay ShellCheck bắt được lỗi và tăng SC2155 , thông báo rằng
export foo="$(mycmd)"
nên được đổi thành
foo=$(mycmd)
export foo
và
local foo="$(mycmd)"
nên được đổi thành
local foo
foo=$(mycmd)
local
quan hệ vào điều này? Ví dụlocal foo=$(bar)
?