Làm cách nào để tôi nhận được giá trị đầu ra và thoát của một mạng con khi sử dụng, bash -e,?


70

Hãy xem xét các mã sau đây

ngoài-scope.sh

#!/bin/bash
set -e
source inner-scope.sh
echo $(inner)
echo "I thought I would've died :("

bên trong-scope.sh

#!/bin/bash
function inner() { echo "winner"; return 1; }

Tôi đang cố outer-scope.shthoát ra khi có cuộc gọi đến inner(). Kể từ khi $()gọi một lớp vỏ phụ, điều này không xảy ra.

Làm thế nào khác để tôi có được đầu ra của một hàm trong khi duy trì thực tế là hàm có thể thoát với mã thoát khác không?

Câu trả lời:


102

$()giữ nguyên trạng thái thoát; bạn chỉ cần sử dụng nó trong một câu lệnh không có trạng thái của riêng nó, chẳng hạn như một bài tập.

đầu ra = $ (bên trong)

Sau này, $?sẽ chứa trạng thái thoát của innervà bạn có thể sử dụng tất cả các loại kiểm tra cho nó:

output=$(inner) || exit $?
echo $output

Hoặc là:

if ! output=$(inner); then
    exit $?
fi
echo $output

Hoặc là:

if output=$(inner); then
    echo $output
else
    exit $?
fi

(Lưu ý: Một khoảng trống exitkhông có đối số tương đương với exit $?- nghĩa là, nó thoát với trạng thái thoát lệnh cuối cùng. Tôi chỉ sử dụng biểu mẫu thứ hai để rõ ràng.)


Ngoài ra, đối với hồ sơ: sourcehoàn toàn không liên quan trong trường hợp này. Bạn chỉ có thể xác định inner()trong outer-scope.shtệp, với kết quả tương tự.


Tại sao lại như vậy, mặc dù $? chứa trạng thái thoát của $ () mà tập lệnh không tự động thoát (cho rằng -e được đặt)? EDIT: nevermind, tôi nghĩ rằng bạn đã trả lời câu hỏi của tôi, cảm ơn!
jabalsad

Tôi không chắc. (Tôi chưa thử nghiệm bất kỳ điều nào ở trên.) Nhưng có một số hạn chế đối với -e, tất cả được giải thích trong trang của bash; Ngoài ra, nếu bạn đang hỏi về điều echo $()đó, thì có thể là do mã thoát của các khung con bị bỏ qua khi dòng - echolệnh - có mã thoát (thường là 0).
grawity

1
Hmm, khi tôi gõ if ! $(exit 1) ; then echo $?; fi, tôi nhận được 0. Không chắc chắn iflà cách để đi nếu bạn cần bảo tồn giá trị thoát đó.
Ron Burk

@grawity - Cái này có hoạt động với Dash không? Tôi đang cố gắng khắc phục một số hành vi Autoconf gây phiền nhiễu (cụ thể là báo cáo Autoconf thành công khi trình biên dịch của Sun hoặc IBM in tùy chọn bất hợp pháp cho thiết bị đầu cuối).
jww

3
if ! output=$(inner); then exit $?; fisẽ thoát với mã trả về là 0 vì $?sẽ cung cấp mã trả về !thay vì mã trả về của inner. Bạn có thể có được hành vi mong muốn với if output=$(inner); then : ; else exit $?; finhưng rõ ràng là dài dòng hơn
SJL

26

Xem BashFAQ / 002 :

Nếu bạn muốn cả hai (trạng thái đầu ra và thoát):

output=$(command)
status=$? 

Một trường hợp đặc biệt

Lưu ý về một trường hợp khó khăn với các biến cục bộ của hàm, so sánh đoạn mã sau:

f() { local    v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }

Chúng tôi sẽ nhận được:

$ f     # fooled by 'local' with inline initialization
output:data, status:0

$ g     # a good one
output:data, status:1

Tại sao?

Khi đầu ra của một lớp con được sử dụng để khởi tạo một localbiến, trạng thái thoát không còn là của lớp con, mà là của lệnh , rất có thể là .local 0

Xem thêm https://stackoverflow.com/a/4421282/537554


3
Mặc dù điều này không thực sự trả lời câu hỏi, nhưng điều này rất hữu ích với tôi ngày hôm nay, vì vậy +1.
Fourpastmidnight

1
Trạng thái thoát cho các lệnh bash luôn là trạng thái của lệnh cuối cùng được thực thi. Khi chúng ta dành quá nhiều thời gian cho các ngôn ngữ được gõ mạnh, thật dễ dàng để quên rằng "cục bộ" không phải là một công cụ xác định kiểu mà chỉ là một lệnh khác. Cảm ơn bạn đã nhắc lại điểm này ở đây, đã giúp tôi ngày hôm nay.
markeissler

2
Ồ, tôi đã gặp phải vấn đề chính xác này ngay bây giờ và bạn đã xóa nó. Cảm ơn!
krb686

4
#!/bin/bash
set -e
source inner-scope.sh
foo=$(inner)
echo $foo
echo "I thought I would've died :("

Bằng cách thêm vào echo, lớp con không đứng độc lập (không được kiểm tra riêng) và không hủy bỏ. Bài tập cắt ngang vấn đề này.

Bạn cũng có thể làm điều này và chuyển hướng đầu ra thành một tệp để xử lý nó sau.

tmpfile=$( mktemp )
inner > $tmpfile
cat $tmpfile
rm $tmpfile

Tất nhiên, $ tmpfile tiếp tục tồn tại trong biến thể thứ hai ...
Daniel Beck
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.