Làm thế nào là trạng thái trả về của một gán biến được xác định?


10

Tôi đã thấy các cấu trúc trong các kịch bản như thế này:

if somevar="$(somecommand 2>/dev/null)"; then
...
fi

Đây có phải là tài liệu ở đâu đó? Làm thế nào là trạng thái trả về của một biến được xác định và làm thế nào nó liên quan đến thay thế lệnh? (Chẳng hạn, tôi có nhận được kết quả tương tự if echo "$(somecommand 2>/dev/null)"; thenkhông?)

Câu trả lời:


12

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.cmd4E=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, Felixvà / 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 Tkhô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)cmd1cmd3
    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.cmd1cmd3cmd2TCcmd0


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

    0
      Tấ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 -ptù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 localkhi 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)

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, localhoặ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

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

local foo="$(mycmd)"

nên được đổi thành

local foo
foo=$(mycmd)

1
Tuyệt vời cảm ơn bạn! Đối với điểm thưởng, bạn có biết làm thế nào localquan hệ vào điều này? Ví dụ local foo=$(bar)?
tự đại diện

1
Đối với ví dụ thứ hai (chỉ FOO=$(bar)) đáng chú ý rằng về mặt lý thuyết cả trạng thái thoát chuyển nhượng có thể đóng vai trò, xem unix.stackexchange.com/a/341013/117599
phk

1
@Wildcard: Tôi rất vui vì bạn thích nó. Tôi vừa cập nhật lại; một phần lớn của phiên bản mà bạn vừa đọc là sai. Chừng nào bạn còn ở đây, bạn nghĩ gì? Đây có phải là một lỗi trong bash, A=foo cmdchạy cmdngay cả khi Achỉ đọc?
G-Man nói 'Phục hồi Monica'

1
@phk: (1) Lý thuyết thú vị, nhưng tôi không chắc nó có ý nghĩa như thế nào. Hãy xem xét lại ví dụ ngay trước khi tiêu đề bất ngờ của tôi. Nếu Achỉ đọc, lệnh C=value₁ A=value₂ T=value₃sẽ đặt Cnhưng không T(và dĩ nhiên Alà không được đặt) - shell kết thúc xử lý dòng lệnh, bỏ qua T=value₃, vì đó A=value₂là một lỗi. (2) Cảm ơn bạn đã liên kết đến câu hỏi Stack Overflow - Tôi đã đăng các bình luận về nó.
G-Man nói 'Phục hồi Monica'

1
@Wildcard "Đối với điểm thưởng, bạn có biết mối quan hệ địa phương trong vấn đề này như thế nào không?". Có ... local=$(false)có giá trị thoát 0vì (từ trang man) : It is an error to use local when not within a function. The return status is 0 unless local is used outside a function, an invalid name is supplied, or name is a readonly variable.. Điều này là không đủ trollface trên thế giới cho các thiên tài đã thiết kế nó theo cách đó.
David Tonhofer

2

Nó được ghi lại trong Bash ( LESS=+/'^SIMPLE COMMAND EXPANSION' bash):

Nếu có một tên lệnh còn lại sau khi mở rộng .... Nếu không, lệnh thoát. ... Nếu không có lệnh thay thế, lệnh sẽ thoát với trạng thái bằng không.

Nói cách khác (lời của tôi):

Nếu không còn tên lệnh nào sau khi mở rộng và không có lệnh thay thế nào được thực thi, dòng lệnh sẽ thoát với trạng thái bằng không.

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.