Thực hành tốt nhất để sử dụng $? trong bash?


10

Khi tôi đọc câu trả lời này về $? một câu hỏi khác đến với tâm trí.

Có cách thực hành tốt nhất nào cho cách sử dụng $ không? trong bash?


Hãy có một ví dụ:

Chúng tôi có một kịch bản tuyến tính và tôi muốn biết rằng tất cả các lệnh đã được thực hiện ok. Bạn có nghĩ rằng gọi một hàm nhỏ là ổn (hãy gọi nó là "did_it_work"), để kiểm tra mã lỗi và ngắt nếu không.

#!/bin/bash 

function did_it_work {
    code=$1
    if [ "$code" -ne "0" ]
    then
        echo "Error failure: code $code "
        exit 1
    fi
}

dir=some/path

mkdir -p $dir
did_it_work $? 

cd $dir
did_it_work $? 

run_some_command
did_it_work $? 

Cách tiếp cận này tất nhiên có nghĩa là tôi phải tự giải quyết vấn đề nếu có bất kỳ và chạy lại tập lệnh.

Bạn có nghĩ rằng đây là một ý tưởng tốt hoặc có một số thực hành tốt nhất khác để làm điều này?

/Cảm ơn

Câu trả lời:


15

Một cách phổ biến là:

die() {
    IFS=' ' # make sure "$*" is joined with spaces

    # output the arguments if any on stderr:
    [ "$#" -eq 0 ] || printf '%s\n' "$*" 1>&2
    exit 1
}

sau đó bạn sử dụng nó như thế này:

mkdir -p some/path || die "mkdir failed with status $?"

Hoặc nếu bạn muốn nó bao gồm trạng thái thoát, bạn có thể thay đổi nó thành:

die() {
    last_exit_status=$?
    IFS=' '
    printf '%s\n' "FATAL ERROR: $* (status $last_exit_status)" 1>&2
    exit 1
}

và sau đó sử dụng nó dễ dàng hơn một chút:

mkdir -p some/path || die "mkdir failed"

Khi thất bại, mkdirnhiều khả năng đã đưa ra thông báo lỗi, do đó thông báo thứ hai có thể được xem là dư thừa và bạn chỉ có thể làm:

mkdir -p some/path || exit   # with the same (failing) exit status as mkdir's
mkdir -p some/path || exit 1 # with exit status 1 always

(hoặc sử dụng biến thể đầu tiên dieở trên mà không cần đối số)

Chỉ trong trường hợp bạn chưa từng thấy command1 || command2trước đó, nó sẽ chạy command1và nếu command1thất bại, nó sẽ chạy command2.

Vì vậy, bạn có thể đọc nó như "làm cho thư mục hoặc chết".

Ví dụ của bạn sẽ giống như:

mkdir -p some/path || die "mkdir failed"
cd some/path || die "cd failed"
some_command || die "some_command failed"

Hoặc bạn có thể căn chỉnh diesthêm ở bên phải để mã chính rõ ràng hơn.

mkdir -p some/path         || die "mkdir failed"
cd some/path               || die "cd failed"
some_command               || die "some_command failed"

Hoặc trên dòng sau khi dòng lệnh dài:

mkdir -p some/path ||
  die "mkdir failed"

cd some/path ||
  die "cd failed"

some_command ||
  die "some_command failed"

Ngoài ra, nếu bạn định sử dụng tên some/pathnhiều lần, hãy lưu trữ nó trong một biến để bạn không phải tiếp tục nhập tên và có thể dễ dàng thay đổi tên nếu bạn cần. Và khi truyền các đối số biến cho các lệnh, hãy đảm bảo sử dụng --dấu phân cách tùy chọn để đối số không được lấy làm tùy chọn nếu bắt đầu bằng -.

dir=some/path
mkdir -p -- "$dir"         || die "Cannot make $dir"
cd -P -- "$dir"            || die "Cannot cd to $dir"
some_command               || die "Cannot run some_command"

9

Bạn có thể viết lại mã của bạn như thế này:

#!/bin/bash
function try {
    "$@"
    code=$?
    if [ $code -ne 0 ]
    then
        echo "$1 did not work: exit status $code"
        exit 1
    fi
}

try mkdir -p some/path
try cd some/path
try run_some_command

Nếu bạn thực sự không cần phải đăng nhập mã lỗi, nhưng chỉ cần lệnh thành công hay không, bạn có thể rút ngắn try()hơn nữa như vậy:

function try {
    if ! "$@"
    then
        echo "$1 did not work"
        exit 1
    fi
}

Bạn cũng có thể sử dụng mã theo định dạng này. Chức năng <pre> thử {
BillThor

Nếu bạn sử dụng chức năng này nội tuyến và không muốn trở về từ phía thực, bạn có thể thay thế return $?bằng :nội dung.
BillThor

8

Nếu bạn thực sự muốn exitgặp lỗi và đang sử dụng Bash, thì bạn cũng nên xem xét set -e. Từ help set:

-e Thoát ngay lập tức nếu một lệnh thoát với trạng thái khác không.

Điều này tất nhiên không cung cấp cho bạn tính linh hoạt của hàm did_it_work (), nhưng đây là một cách dễ dàng để đảm bảo tập lệnh bash của bạn dừng khi có lỗi mà không cần thêm nhiều cuộc gọi vào hàm mới.


set -elà hữu ích. Có một số lệnh trả về khác không trong các trường hợp thông thường (ví dụ, diff). Khi tôi đang sử dụng set -e trong một kịch bản mà tôi mong đợi sự trở lại khác thường, tôi sẽ làm command || true.
Shawn J. Goff

2
Hơn nữa, ngay cả khi bạn sử dụng set -e, bạn có thể thiết lập trình xử lý ngoại lệ của Google để bắt tất cả các lỗi trap did_it_work EXIT.
Gilles 'SO- ngừng trở thành ác quỷ'

1
Đây là một phần của POSIX, không phải là một tính năng cụ thể của bash. Sử dụng nó nhưng hãy chú ý đến một số cạm bẫy mywiki.wooledge.org/BashFAQ/105
kmkaplan

@ ShawnJ.Goff Tôi thích làm hơn command && true. Như thế giá trị trả lại không thay đổi.
kmkaplan

@kmkaplan Nhận xét cuối cùng của bạn không có ý nghĩa gì với tôi. Mục đích của command || trueviệc ngăn chặn set -ethoát khỏi tập lệnh nếu commandtrả về mã thoát khác không. Nó thay đổi mã thoát vì chúng ta cần. Điều duy nhất command && truethực hiện là chạy true(trả về mã thoát 0) nếu lệnh `thành công (trả về mã thoát không) - đó là một lệnh không hoàn chỉnh.
tripleee
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.