Làm thế nào để có điều kiện làm một cái gì đó nếu một lệnh thành công hoặc thất bại


283

Làm thế nào tôi có thể làm một cái gì đó như thế này trong bash?

if "`command` returns any error";
then
    echo "Returned an error"
else
    echo "Proceed..."
fi

Câu trả lời:


359

Làm thế nào để có điều kiện làm một cái gì đó nếu một lệnh thành công hoặc thất bại

Đó chính xác là những gì iftuyên bố của bash :

if command ; then
    echo "Command succeeded"
else
    echo "Command failed"
fi

Thêm thông tin từ các bình luận: bạn không cần sử dụng cú pháp [... ]trong trường hợp này. [bản thân nó là một lệnh, rất gần tương đương với test. Đây có lẽ là lệnh phổ biến nhất để sử dụng trong một if, điều này có thể dẫn đến giả định rằng đó là một phần của cú pháp của shell. Nhưng nếu bạn muốn kiểm tra xem một lệnh có thành công hay không, hãy sử dụng chính lệnh đó if, như được hiển thị ở trên.

Chỉnh sửa: Trích dẫn câu hỏi ở đầu trang cho rõ ràng (câu trả lời này không xuất hiện gần đầu trang).


11
Lưu ý rằng dấu chấm phẩy rất quan trọng.
Thorbjørn Ravn Andersen

21
Hoặc bạn chỉ có thể đặt thentrên một dòng riêng biệt.
l0b0

36
@Joe: Tôi nghĩ bạn có nghĩa là if ! command ; then ... ; fi. [bản thân nó là một lệnh và nó không cần thiết trong trường hợp này.
Keith Thompson

20
@Joe: Cách của tôi cũng có đức tính chính xác. if [ ! command ]không thực hiện command; nó coi commandnhư một chuỗi và coi nó là đúng vì nó có độ dài khác không. [là một từ đồng nghĩa với testlệnh
Keith Thompson

5
Úi. Bạn hoàn toàn đúng.
Joe

133

Đối với những điều nhỏ mà bạn muốn xảy ra nếu lệnh shell hoạt động, bạn có thể sử dụng &&cấu trúc:

rm -rf somedir && trace_output "Removed the directory"

Tương tự như vậy đối với những điều nhỏ mà bạn muốn xảy ra khi lệnh shell bị lỗi, bạn có thể sử dụng ||:

rm -rf somedir || exit_on_error "Failed to remove the directory"

Hoặc cả hai

rm -rf somedir && trace_output "Removed the directory" || exit_on_error "Failed to remove the directory"

Có thể không khôn ngoan khi làm rất nhiều với các cấu trúc này, nhưng đôi khi chúng có thể làm cho dòng kiểm soát rõ ràng hơn rất nhiều.


3
Chúng ngắn hơn và (ít nhất là trong một số vỏ) nhanh hơn. Tôi rùng mình nhớ một kịch bản cài đặt Ultrix quái vật được viết chỉ bằng những công trình có điều kiện này mà tôi đã từng cố gắng giải mã ...
vonbrand

101

Kiểm tra giá trị của $?, trong đó có kết quả thực hiện lệnh / hàm gần đây nhất:

#!/bin/bash

echo "this will work"
RESULT=$?
if [ $RESULT -eq 0 ]; then
  echo success
else
  echo failed
fi

if [ $RESULT == 0 ]; then
  echo success 2
else
  echo failed 2
fi

11
Mặc dù về mặt kỹ thuật chính xác (và do đó không đảm bảo một downvote), nó không sử dụng ifthành ngữ của Bash . Tôi thích câu trả lời của Keith Thompson.
janmoesen

16
Có những lợi ích cho thành ngữ này - nó bảo tồn giá trị trả về. Trong tất cả, tôi thấy cái này mạnh hơn, dù dài dòng hơn. Nó cũng dễ đọc hơn.
taxilian

2
"Bash's if idiom" là gì?
Bây giờ là

3
@Nowaker Thực tế rằng mục đích duy nhất iflà để làm điều này. Các điều kiện kiểm soát dòng chảy trong Bash tất cả kiểm tra $?đằng sau hậu trường; đó là những gì họ làm. Rõ ràng kiểm tra giá trị của nó là không cần thiết trong phần lớn các trường hợp, và thường là một antipotype mới bắt đầu.
tripleee

1
@lunakid Nếu bạn không quan tâm đến mã thoát, if ! cmdthì tốt thôi. Mặt khác, một sự sắp xếp phổ biến là sử dụng một elsemệnh đề. Bạn không thể có một sản phẩm nào thennhưng đôi khi bạn thấy then : nothing; elsenơi :không có ý nghĩa. truecũng sẽ làm việc ở đó
tripleee

47

Điều này làm việc cho tôi:

command && echo "OK" || echo "NOK"

nếu commandthành công, thì echo "OK"được thực thi và vì nó thành công, nên việc thực thi dừng ở đó. Mặt khác, &&được bỏ qua, và echo "NOK"được thực thi.


5
Nếu bạn muốn làm gì đó nếu thất bại và giữ mã thoát (để hiển thị trong dấu nhắc lệnh hoặc kiểm tra trong tập lệnh), bạn có thể làm điều này: command && echo "OK" || c=$?; echo "NOK"; $(exit $c)
Sam Hasler

2
@ Sam-Hasler: không nên như command && echo "OK" || (c=$?; echo "NOK"; (exit $c))vậy?
jrw32982

9
Ngoài ra, nếu bộ echo "OK"phận có thể tự thất bại, thì điều này tốt hơn:command && (echo "OK"; exit 0) || (c=$?; echo "NOK"; (exit $c))
jrw32982

@ jrw32982, Nice, tôi đã sử dụng công trình cũ, nhưng không phải công trình sau.
Sam Hasler

Câu trả lời thực sự là trong các ý kiến, cảm ơn tất cả!
Joshua Pinter

6

Cần lưu ý rằng if...then...fi&&/ ||loại giao dịch tiếp cận với trạng thái thoát được trả về bằng lệnh chúng tôi muốn kiểm tra (0 khi thành công); tuy nhiên, một số lệnh không trả về trạng thái thoát khác không nếu lệnh không thành công hoặc không thể xử lý đầu vào. Điều này có nghĩa là các cách tiếp cận thông thường if&&/ ||sẽ không hoạt động đối với các lệnh cụ thể đó.

Chẳng hạn, trên Linux GNU filevẫn tồn tại với 0 nếu nó nhận được tệp không tồn tại dưới dạng đối số và findkhông thể xác định vị trí người dùng tệp được chỉ định.

$ find . -name "not_existing_file"                                          
$ echo $?
0
$ file ./not_existing_file                                                  
./not_existing_file: cannot open `./not_existing_file' (No such file or directory)
$ echo $?
0

Trong các trường hợp như vậy, một cách tiềm năng mà chúng ta có thể xử lý tình huống là bằng cách đọc stderr/ stdintin nhắn, ví dụ: những cách được trả về bằng filelệnh hoặc phân tích đầu ra của lệnh như trong find. Đối với mục đích đó, casetuyên bố có thể được sử dụng.

$ file ./doesntexist  | while IFS= read -r output; do                                                                                                                  
> case "$output" in 
> *"No such file or directory"*) printf "%s\n" "This will show up if failed";;
> *) printf "%s\n" "This will show up if succeeded" ;;
> esac
> done
This will show up if failed

$ find . -name "doesn'texist" | if ! read IFS= out; then echo "File not found"; fi                                                                                     
File not found

2

Lỗi dễ mắc nhất mà tôi có thể gặp phải là:

  • Đầu tiên, nhận được giá trị. Giả sử bạn làm một cái gì đó như:

RR=$?

Bây giờ, không chỉ đối với tình huống này, mà cả những vấn đề khác mà bạn có thể gặp phải, hãy xem xét:

biến xác định:

$ AA=1 ; if (( "10#0${AA}" == 1 )) ; then echo yes ; else echo no ; fi

Trả lời có

$ AA=1 ; if (( "10#0${AA}" != 1 )) ; then echo yes ; else echo no ; fi

Trả lời: không

biến không xác định:

$ AA=1 ; if (( "10#0${BB}" == 1 )) ; then echo yes ; else echo no ; fi

Trả lời: không

$ AA=1 ; if (( "10#0${BB}" != 1 )) ; then echo yes ; else echo no ; fi

Trả lời có

$ AA=1 ; if (( "10#0${BB}" == 0 )) ; then echo yes ; else echo no ; fi

Trả lời có

Điều này ngăn chặn tất cả các loại lỗi.

Bạn có thể nhận thức được tất cả các cú pháp, nhưng ở đây một số mẹo:

  • Sử dụng dấu ngoặc kép. Tránh một "blank"để được nothing.
  • Các ký hiệu hiện đại mới cho các biến là ${variable}.
  • Thêm một số 0 được nối trước số của bạn cũng tránh "không có số nào cả".
  • Nhưng chờ đợi, thêm một số không làm cho số trở thành base-8. Bạn sẽ gặp một lỗi như:
    • value too great for base (error token is "08")cho các số trên 7. Đó là khi 10#đi vào chơi:
    • 10#buộc số lượng được base-10.

1

Bạn có thể làm được việc này:

if ($( ping 4.4.4.4 -c1 > /dev/null )) ; then
  echo "ping response succsess!!!"
fi

9
Điều đó làm việc nhưng bị ảnh hưởng. Bạn đang chạy ping trong một lớp con của một lớp con, đầu ra của pingđược ghi lại để xem chạy nó dưới dạng một lệnh. Nhưng vì đầu ra được chuyển hướng đến / dev / null nên sẽ luôn là chuỗi rỗng. Vì vậy, bạn không chạy gì trong một lớp con, có nghĩa là trạng thái thoát trước đó (của lớp con thay thế lệnh, đó là ping) sẽ được giữ lại. Rõ ràng, cách chính xác là if ping ...; thenở đây.
Stéphane Chazelas

1

Như đã lưu ý ở nơi khác trong chủ đề này, câu hỏi ban đầu về cơ bản tự trả lời. Dưới đây là một minh họa cho thấy các ifđiều kiện cũng có thể được lồng nhau.

Ví dụ này sử dụng ifđể kiểm tra nếu một tệp tồn tại và nếu đó là một tệp thông thường. Nếu những điều kiện đó là đúng, thì hãy kiểm tra xem nó có kích thước lớn hơn 0 hay không.

#!/bin/bash

echo "Which error log are you checking today? "
read answer

if [ -f /opt/logs/$answer*.errors ]
    then
        if [ -s /opt/logs/$answer*.errors ]
            then
                echo "Content is present in the $answer error log file."
            else
                echo "No errors are present in the $answer error log file."
        fi
    else
        echo "$answer does not have an error log at this time."
fi

2
Đó là những gì câu trả lời của bạn làm, nhưng câu trả lời của bạn không giải quyết được câu hỏi.
Jeff Schaller

@JeffSchaller, cảm ơn bạn đã lưu ý. Tôi chỉnh sửa bài viết của mình để bao gồm một tài liệu tham khảo cho câu hỏi.
Quartzrowartz

1
#!/bin/bash

if command-1 ; then
   echo "command-1 succeeded and now running from this block command-2"
   command-2
else
   echo "command-1 failed and now running from this block command-3"
   command-3
fi

3
Điều này trông khá giống với câu trả lời được chấp nhận hiện có ; vui lòng ghi công việc bạn đang sử dụng lại hoặc thêm nỗ lực của bạn vào câu trả lời của bạn.
Jeff Schaller

-3

Điều này có thể được thực hiện đơn giản theo cách này vì $?cung cấp cho bạn trạng thái của lệnh cuối cùng được thực thi.

Vì vậy, nó có thể là

#!/bin/sh

... some command ...

if [ $? == 0 ] ; then
  echo '<the output message you want to display>'
else 
  echo '<failure message>'
fi

1
Downvote: Điều này chỉ đơn giản là diễn giải một câu trả lời trước đó đã nhận được sự chỉ trích một cách đúng đắn vì không phổ biến.
tripleee 4/11/2016
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.