thoát khỏi kịch bản shell từ một subshell


30

Hãy xem xét đoạn trích này:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

Thông thường khi funcđược gọi nó sẽ khiến tập lệnh kết thúc, đó là hành vi dự định. Tuy nhiên, nếu nó được thực thi trong một vỏ con, chẳng hạn như trong

result=`func`

nó sẽ không thoát khỏi tập lệnh. Điều này có nghĩa là mã gọi phải kiểm tra trạng thái thoát của hàm mỗi lần. Có cách nào để tránh điều này không? Đây có phải là những gì set -echo?


1
Tôi muốn một chức năng "dừng" in thông báo lên stderr và dừng tập lệnh, nhưng nó không dừng khi chức năng gọi dừng được thực thi trong lớp vỏ phụ, như trong ví dụ
Ernest AC

2
Tất nhiên, vì nó thoát ra khỏi subshell chứ không phải cái hiện tại. Đơn giản chỉ cần gọi hàm trực tiếp : func.

1
tôi không thể gọi nó trực tiếp vì nó trả về một chuỗi phải được lưu trữ trong một biến
Ernest AC

1
@ErnestAC Vui lòng cung cấp tất cả các chi tiết trong câu hỏi ban đầu. Hàm trên không trả về một chuỗi.

1
@htor Tôi đã thay đổi ví dụ
Ernest AC

Câu trả lời:


10

Bạn có thể giết shell gốc ( kill $$) trước khi gọi exitvà điều đó có thể sẽ hoạt động. Nhưng:

  • nó có vẻ khá xấu với tôi
  • nó sẽ bị hỏng nếu bạn có một subshell thứ hai trong đó, tức là sử dụng một subshell bên trong một subshell.

Thay vào đó, bạn có thể sử dụng một trong một số cách để trả lại giá trị trong Câu hỏi thường gặp của Bash . Hầu hết trong số họ không quá tuyệt vời, thật không may. Bạn có thể bị kẹt khi kiểm tra lỗi sau mỗi lần gọi hàm ( -ecó rất nhiều vấn đề ). Hoặc là, hoặc chuyển sang Perl.


5
Cảm ơn. Tôi muốn chuyển sang Python hơn.
Ernest AC

2
Như tôi viết, đó là năm 2019. Nói ai đó "chuyển sang Perl" là vô lý. Xin lỗi vì đã gây tranh cãi, nhưng bạn có nói với ai đó thất vọng với 'C' để chuyển sang Cobol, tương đương IMO không? Như Ernest chỉ ra, Python là một lựa chọn tốt hơn nhiều. Sở thích của tôi sẽ là Ruby. Dù bằng cách nào, bất cứ điều gì trừ Perl.
Graham Nicholls

38

Bạn có thể quyết định rằng trạng thái thoát 77 chẳng hạn có nghĩa là thoát bất kỳ cấp độ con nào và làm

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -Ekết hợp với ERRbẫy giống như một phiên bản cải tiến set -etrong đó nó cho phép bạn xác định xử lý lỗi của riêng mình.

Trong zsh, bẫy ERR được kế thừa tự động, do đó bạn không cần set -E, bạn cũng có thể xác định bẫy là TRAPERR()hàm và sửa đổi chúng thông qua $functions[TRAPERR], nhưfunctions[TRAPERR]="echo was here; $functions[TRAPERR]"


1
Giải pháp thú vị! Rõ ràng thanh lịch hơn kill $$.

3
Một điều cần chú ý, cái bẫy này sẽ không xử lý các lệnh được nội suy, ví dụ echo "$(exit 77)"; kịch bản sẽ tiếp tục như thể chúng ta đã viếtecho ""
Warbo 30/03/2016

Can thiệp! Có bất kỳ may mắn nào trên bash (khá cũ) không có -E không? có lẽ chúng ta phải dùng đến việc xác định bẫy trên tín hiệu USER và sử dụng lệnh kill cho tín hiệu đó? Tôi cũng sẽ thực hiện một số nghiên cứu ...
Olivier Dulac

Làm thế nào để biết, khi không ở trong bẫy phụ, để trả về 1 thay vì 77?
ceving

7

Thay vào đó kill $$, bạn cũng có thể thử kill 0, nó sẽ hoạt động trong trường hợp các mạng con lồng nhau (tất cả người gọi và quá trình bên sẽ nhận được tín hiệu) nhưng nó vẫn tàn bạo và xấu xí.


2
Điều này sẽ không giết quá trình id 0?
Ernest AC

5
Điều đó sẽ giết toàn bộ nhóm quy trình. Bạn có thể nhấn những thứ bạn không muốn (ví dụ: nếu bạn đã bắt đầu một số thứ trong nền).
derobert

2
@ErnestAC xem manpage kill (2), pids 0 có ý nghĩa đặc biệt.
derobert

0

Thử đi ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "shell..."
func $1

echo "subshell..."
result=`func $1`

echo "shell..."
echo "result=$result"

Kết quả tôi nhận được là ...

# test_exitsubshell true
shell...
foo
subshell...
shell...
result=foo
# test_exitsubshell false
shell...
something went wrong

Ghi chú

  • Tham số hóa để cho phép ifthử nghiệm truehoặc false(xem 2 lần chạy)
  • Khi ifkiểm tra là false, chúng tôi không bao giờ đạt đến subshell.

Điều này rất giống với ý tưởng ban đầu mà người dùng đã đăng và nói rằng nó không hoạt động. Tôi không nghĩ rằng điều này làm việc cho trường hợp subshell. Thử nghiệm của bạn sử dụng các lối thoát sai sau trường hợp "shell" và không bao giờ đến trường hợp thử nghiệm "subshell". Tôi tin rằng nó sẽ thất bại trong trường hợp đó vì lớp con sẽ thoát khỏi lệnh gọi "exit 1" nhưng không truyền lỗi sang lớp vỏ bên ngoài.
mắc kẹt

0

(Bash trả lời cụ thể) Bash không có khái niệm về ngoại lệ. Tuy nhiên, với set -o errexit (hoặc tương đương: set -e) ở mức ngoài cùng, lệnh không thành công sẽ dẫn đến việc thoát khỏi subshell với trạng thái thoát khác không. Nếu đây là một tập hợp các chuỗi con được lồng mà không có điều kiện xung quanh việc thực thi các chuỗi con đó, thì nó sẽ "cuộn" toàn bộ tập lệnh và thoát một cách hiệu quả.

Điều này có thể khó khăn khi cố gắng đưa các bit của mã bash khác nhau vào một tập lệnh lớn hơn. Một khối bash có thể tự hoạt động tốt, nhưng khi được thực hiện theo errexit (hoặc không có errexit), hãy hành xử theo những cách không ngờ tới.

[192.168.13.16 (f0f5e19e) ~ 22:58:22] # bash -o errexit / tmp / foo
đã xảy ra lỗi
[192.168.13.16 (f0f5e19e) ~ 22:58:31] # bash / tmp / foo
đã xảy ra lỗi
Nhưng dù sao chúng tôi cũng đến đây
[192.168.13.16 (f0f5e19e) ~ 22:58:37] # mèo / tmp / foo
#! / bin / bash
dừng lại () {
    tiếng vang "$ {1}"
    thoát 1
}

nếu sai; sau đó
    tiếng vang "foo"
khác
    (
        dừng lại "một cái gì đó đã đi sai"
    )
    echo "Nhưng chúng tôi đã đến đây bằng mọi cách"
fi
[192.168.13.16 (f0f5e19e) ~ 22:58:40] #

-2

Ví dụ của tôi để thoát trong một lớp lót:

COMAND || ( echo "ERROR – executing COMAND, exiting..." ; exit 77 );[ "$?" -eq 77 ] && exit

1
Đây dường như không thực sự là một câu trả lời sẽ hoạt động với lệnh chạy trong lớp vỏ phụ theo yêu cầu của OP ... Điều đó nói trong khi tôi không đồng ý với số phiếu giảm cho câu trả lời. Bỏ phiếu mà không có bình luận hoặc lý do cũng không có ích như câu trả lời xấu.
DVS
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.