Trả về một boolean từ hàm Bash


211

Tôi muốn viết một hàm bash để kiểm tra xem một tệp có các thuộc tính nhất định và trả về đúng hay sai. Sau đó, tôi có thể sử dụng nó trong các tập lệnh của mình trong "nếu". Nhưng tôi nên trở về cái gì?

function myfun(){ ... return 0; else return 1; fi;}

sau đó tôi sử dụng nó như thế này:

if myfun filename.txt; then ...

Tất nhiên điều này không hiệu quả. Làm thế nào điều này có thể được thực hiện?


3
bỏ functiontừ khóa, myfun() {...}đủ
glenn jackman

2
Điều quan trọng iflà trạng thái thoát không của myfun: nếu myfunthoát với 0, then ...được thực thi; nếu nó là bất cứ điều gì khác else ... được thực thi.
Eelvex

7
@nhed: functiontừ khóa là một bashism và sẽ gây ra lỗi cú pháp trong một số shell khác. Về cơ bản, nó không cần thiết hoặc bị cấm, vậy tại sao lại sử dụng nó? Nó thậm chí không hữu ích như một mục tiêu grep, vì nó có thể không ở đó ( ()thay vào đó là grep ).
Gordon Davisson

3
@GordonDavisson: gì? Có vỏ nào khác không? ;-)
nhed

Vui lòng không sử dụng 0 và 1. Xem stackoverflow.com/a/43840545/117471
Bruno Bronosky

Câu trả lời:


333

Sử dụng 0 cho đúng và 1 cho sai.

Mẫu vật:

#!/bin/bash

isdirectory() {
  if [ -d "$1" ]
  then
    # 0 = true
    return 0 
  else
    # 1 = false
    return 1
  fi
}


if isdirectory $1; then echo "is directory"; else echo "nopes"; fi

Biên tập

Từ nhận xét của @ amichair, những điều này cũng có thể

isdirectory() {
  if [ -d "$1" ]
  then
    true
  else
    false
  fi
}


isdirectory() {
  [ -d "$1" ]
}

4
Không, bạn không cần phải làm điều đó - xem mẫu.
Erik

46
Để dễ đọc hơn, bạn có thể sử dụng lệnh 'true' (không làm gì và hoàn thành thành công, tức là trả về 0) và lệnh 'false' (không làm gì và hoàn thành không thành công, tức là trả về giá trị khác không). Ngoài ra, một hàm kết thúc mà không có câu lệnh return rõ ràng trả về mã thoát của lệnh được thực thi cuối cùng, vì vậy trong ví dụ trên, thân hàm có thể được giảm xuống chỉ còn [ -d "$1" ].
amichair

24
Bengt: thật có ý nghĩa khi bạn nghĩ về nó như mã lỗi của Mã số: mã lỗi 0 = mọi thứ đều ổn = 0 lỗi; mã lỗi 1 = điều chính mà cuộc gọi này được cho là đã thất bại; khác: thất bại! tìm kiếm nó trong trang web
cừu bay ngày

7
Thật ý nghĩa khi bạn xem xét rằng trong lập trình, mọi thứ thường chỉ có thể thành công theo một cách, nhưng có thể thất bại theo những cách vô hạn. Có lẽ không phải là vô hạn, nhưng rất nhiều, tỷ lệ cược được xếp chồng lên chúng tôi. Thành công / Lỗi không phải là boolean. Tôi nghĩ điều này "Sử dụng 0 cho đúng và 1 cho sai." nên đọc "Sử dụng 0 cho thành công và khác không cho thất bại".
Davos

6
Vui lòng không sử dụng 0 và 1. Xem stackoverflow.com/a/43840545/117471
Bruno Bronosky

167

Tại sao bạn nên quan tâm những gì tôi nói mặc dù có câu trả lời hơn 250 câu trả lời

Nó không phải là 0 = true1 = false. Đó là: zero có nghĩa là không có thất bại (thành công)khác không có nghĩa là thất bại (thuộc loại N) .

Mặc dù câu trả lời được chọn là "đúng" về mặt kỹ thuật, vui lòng không đặt return 1** vào mã của bạn cho sai . Nó sẽ có một số tác dụng phụ đáng tiếc.

  1. Các nhà phát triển có kinh nghiệm sẽ nhận ra bạn là một người nghiệp dư (vì lý do dưới đây).
  2. Các nhà phát triển có kinh nghiệm không làm điều này (vì tất cả các lý do dưới đây).
  3. Đó là lỗi dễ bị.
    • Ngay cả các nhà phát triển có kinh nghiệm cũng có thể nhầm 0 và 1 là sai và đúng tương ứng (vì lý do trên).
  4. Nó đòi hỏi (hoặc sẽ khuyến khích) những bình luận không liên quan và lố bịch.
  5. Nó thực sự ít hữu ích hơn các trạng thái trả lại ngầm.

Tìm hiểu một số bash

Các nhãn hiệu bash nói (tôi nhấn mạnh)

trở lại [n]

Làm cho hàm shell dừng thực thi và trả về giá trị n cho trình gọi của nó. Nếu n không được cung cấp , giá trị trả về là trạng thái thoát của lệnh cuối cùng được thực thi trong hàm.

Do đó, chúng ta không phải sử dụng 0 và 1 để chỉ đúng và sai. Thực tế là họ làm như vậy về cơ bản là kiến ​​thức tầm thường chỉ hữu ích cho việc gỡ lỗi mã, câu hỏi phỏng vấn và thổi bùng tâm trí của người mới.

Hướng dẫn bash cũng nói

mặt khác, trạng thái trả về của hàm là trạng thái thoát của lệnh cuối cùng được thực thi

Hướng dẫn bash cũng nói

( $? ) Mở rộng đến trạng thái thoát của đường ống tiền cảnh được thực hiện gần đây nhất .

Whoa, đợi đã. Đường ống? Hãy chuyển sang hướng dẫn sử dụng bash một lần nữa.

Đường ống là một chuỗi gồm một hoặc nhiều lệnh được phân tách bằng một trong các toán tử điều khiển '|' hoặc '| &'.

Đúng. Họ nói 1 lệnh là một đường ống dẫn. Do đó, cả 3 trích dẫn này đều nói giống nhau.

  • $? cho bạn biết những gì đã xảy ra cuối cùng.
  • Nó bong bóng lên.

Câu trả lời của tôi

Vì vậy, trong khi @Kambus chứng minh rằng với một chức năng đơn giản như vậy, không returncần thiết chút nào. Tôi nghĩ là đơn giản phi thực tế so với nhu cầu của hầu hết những người sẽ đọc này.

Tại sao return?

Nếu một hàm sẽ trả về trạng thái thoát lệnh cuối cùng của nó, tại sao lại sử dụng return? Bởi vì nó gây ra một chức năng để ngừng thực thi.

Dừng thực thi trong nhiều điều kiện

01  function i_should(){
02      uname="$(uname -a)"
03
04      [[ "$uname" =~ Darwin ]] && return
05
06      if [[ "$uname" =~ Ubuntu ]]; then
07          release="$(lsb_release -a)"
08          [[ "$release" =~ LTS ]]
09          return
10      fi
11
12      false
13  }
14
15  function do_it(){
16      echo "Hello, old friend."
17  }
18
19  if i_should; then
20    do_it
21  fi

Những gì chúng ta có ở đây là ...

Dòng 04là trả về [-ish] rõ ràng vì RHS &&chỉ được thực thi nếu LHS đúng

Dòng 09trả về đúng hoặc sai khớp với trạng thái của dòng08

Dòng 13trả về sai vì dòng12

(Vâng, điều này có thể được đánh xuống, nhưng toàn bộ ví dụ là có thể.)

Một mô hình phổ biến khác

# Instead of doing this...
some_command
if [[ $? -eq 1 ]]; then
    echo "some_command failed"
fi

# Do this...
some_command
status=$?
if ! $(exit $status); then
    echo "some_command failed"
fi

Lưu ý cách thiết lập một statusbiến làm sáng tỏ ý nghĩa của $?. (Tất nhiên bạn biết những gì $?phương tiện, nhưng ai đó ít hiểu biết hơn bạn sẽ phải Google nó một ngày nào đó. Trừ phi mã của bạn đang làm giao dịch tần số cao, hiển thị một số tình yêu , đặt biến.) Tuy nhiên, take-away thực sự là "nếu không tồn tại trạng thái "hoặc ngược lại" nếu trạng thái thoát "có thể được đọc thành tiếng và giải thích ý nghĩa của chúng. Tuy nhiên, cái cuối cùng đó có thể là một chút quá tham vọng bởi vì nhìn thấy từ này exitcó thể khiến bạn nghĩ rằng nó đang thoát khỏi kịch bản, trong khi thực tế, nó đang thoát khỏi $(...)subshell.


** Nếu bạn hoàn toàn khăng khăng sử dụng return 1sai, tôi khuyên bạn ít nhất nên sử dụng return 255thay thế. Điều này sẽ khiến bản thân tương lai của bạn hoặc bất kỳ nhà phát triển nào khác phải duy trì mã của bạn đặt câu hỏi "tại sao lại là 255?" Sau đó, ít nhất họ sẽ được chú ý và có cơ hội tốt hơn để tránh một sai lầm.


1
@ZeroPhase 1 & 0 cho sai & đúng sẽ là vô lý. Nếu bạn có một kiểu dữ liệu nhị phân, không có lý do cho điều đó. Những gì bạn đang xử lý trong bash là một mã trạng thái phản ánh thành công (số ít) và thất bại (số nhiều). Đó là " ifthành công làm điều này, elselàm điều đó." Thành công ở điểm gì? Có thể đang kiểm tra true / false, có thể kiểm tra chuỗi, số nguyên, tệp, thư mục, quyền ghi, global, regex, grep hoặc bất kỳ lệnh nào khác có thể bị lỗi .
Bruno Bronosky

3
Tránh sử dụng tiêu chuẩn 0/1 làm giá trị trả về chỉ vì nó khó hiểu và dễ bị nhầm lẫn là ngớ ngẩn. Toàn bộ ngôn ngữ shell là khó hiểu và dễ bị nhầm lẫn. Chính Bash sử dụng quy ước 0/1 = true / false trong chính nó truefalsecác lệnh. Đó là, từ khóa theo truenghĩa đen đánh giá thành mã trạng thái 0. Ngoài ra, các câu lệnh if-then theo bản chất hoạt động trên booleans, không phải mã thành công. Nếu một lỗi xảy ra trong một hoạt động boolean, nó sẽ không trả về true hoặc false mà chỉ đơn giản là ngắt thực thi. Nếu không, bạn nhận được dương tính giả (chơi chữ).
Beejor

1
"if! $ (thoát $ status);" - Điều đó xứng đáng được giải thích tốt hơn. Nó không trực quan. Tôi phải suy nghĩ về việc chương trình sẽ thoát ra trước khi in tin nhắn hay không. Phần còn lại của câu trả lời của bạn là tốt, nhưng điều đó làm hỏng nó.
Craig Hicks

1
@BrunoBronosky Tôi đã đọc câu trả lời của bạn và tôi nghĩ rằng tôi hiểu sự khác biệt giữa đường ống nội dung (stdin-> stdout) và mã lỗi trong các giá trị trả về. Tuy nhiên, tôi không hiểu tại sao return 1nên tránh sử dụng . Giả sử tôi có một validatechức năng tôi thấy nó hợp lý return 1nếu xác nhận thất bại. Rốt cuộc, đó là một lý do để dừng việc thực thi tập lệnh nếu nó không được xử lý đúng cách (ví dụ: khi sử dụng set -e).
JepZ

1
@Jepz đó là một câu hỏi tuyệt vời. Bạn đúng đó return 1là hợp lệ. Mối quan tâm của tôi là tất cả các ý kiến ​​ở đây nói rằng 0 0 = true 1 = false là [chèn từ phủ định]. Những người đó có khả năng đọc mã của bạn một ngày nào đó. Vì vậy, đối với họ, việc nhìn thấy return 255(hoặc 42 hoặc thậm chí 2) sẽ giúp họ suy nghĩ về nó nhiều hơn và đừng nhầm lẫn đó là một sự thật. set -evẫn sẽ bắt nó
Bruno Bronosky

31
myfun(){
    [ -d "$1" ]
}
if myfun "path"; then
    echo yes
fi
# or
myfun "path" && echo yes

1
Thế còn phủ định?
einpoklum

Thế còn nó, @einpoklum?
Mark Reed

@MarkReed: Ý tôi là, thêm trường hợp "khác" vào ví dụ của bạn.
einpoklum

"con đường" của tôi | | echo no
Hrobky 12/03/18

13

Hãy cẩn thận khi kiểm tra thư mục chỉ với tùy chọn -d!
nếu biến $ 1 trống, kiểm tra vẫn sẽ thành công. Để chắc chắn, hãy kiểm tra xem biến đó không trống.

#! /bin/bash

is_directory(){

    if [[ -d $1 ]] && [[ -n $1 ]] ; then
        return 0
    else
        return 1
    fi

}


#Test
if is_directory $1 ; then
    echo "Directory exist"
else
    echo "Directory does not exist!" 
fi

1
Tôi không chắc chắn làm thế nào điều này trả lời câu hỏi. Mặc dù thật tuyệt khi biết rằng $ 1 trống có thể trả về giá trị đúng khi trống, nhưng nó không cung cấp bất kỳ thông tin chi tiết nào về cách trả về đúng hoặc sai từ hàm bash. Tôi sẽ đề nghị tạo một câu hỏi mới "Điều gì xảy ra khi bạn thực hiện kiểm tra trên một biến vỏ rỗng?" Và sau đó đăng bài này là câu trả lời.
DRaehal

3
Lưu ý rằng nếu bạn thêm trích dẫn thích hợp vào $1( "$1") thì bạn không cần kiểm tra biến trống. Các [[ -d "$1" ]]sẽ thất bại bởi vì đây ""không phải là một thư mục.
xác

3

Tôi đã gặp một điểm (chưa được đề cập rõ ràng?) Mà tôi đã vấp ngã. Đó là, không phải làm thế nào để trả lại boolean, mà là làm thế nào để đánh giá chính xác nó!

Tôi đã cố nói if [ myfunc ]; then ..., nhưng điều đó đơn giản là sai. Bạn không được sử dụng dấu ngoặc! if myfunc; then ...là cách để làm điều đó

Như tại @Bruno và những người khác nhắc lại, truefalsecác lệnh , không phải giá trị! Điều đó rất quan trọng để hiểu booleans trong shell script.

Trong bài đăng này, tôi đã giải thích và demo bằng cách sử dụng các biến boolean : https://stackoverflow.com/a/55174008/3220983 . Tôi thực sự khuyên bạn nên kiểm tra xem, vì nó liên quan mật thiết với nhau.

Ở đây, tôi sẽ cung cấp một số ví dụ về việc trả về và đánh giá booleans từ các hàm:

Điều này:

test(){ false; }                                               
if test; then echo "it is"; fi                                 

Sản xuất không có tiếng vang đầu ra. (tức là false trả về sai)

test(){ true; }                                                
if test; then echo "it is"; fi                                 

Sản xuất:

it is                                                        

(tức là true trả về đúng)

test(){ x=1; }                                                
if test; then echo "it is"; fi                                 

Sản xuất:

it is                                                                           

Bởi vì 0 (tức là đúng) đã được trả lại ngầm .

Bây giờ, đây là những gì đã làm tôi khó chịu ...

test(){ true; }                                                
if [ test ]; then echo "it is"; fi                             

Sản xuất:

it is                                                                           

test(){ false; }                                                
if [ test ]; then echo "it is"; fi                             

CSONG sản xuất:

it is                                                                           

Sử dụng dấu ngoặc ở đây tạo ra một dương tính giả ! (Tôi suy ra kết quả lệnh "bên ngoài" là 0.)

Điểm chính của bài viết của tôi là: không sử dụng dấu ngoặc để đánh giá hàm boolean (hoặc biến) như bạn muốn kiểm tra tính bằng bình thường, ví dụ như if [ x -eq 1 ]; then...!


2

Sử dụng các lệnh truehoặc falsengay lập tức trước của bạn return, sau đó returnkhông có tham số. Các returnsẽ tự động sử dụng giá trị của lệnh cuối cùng của bạn.

Cung cấp các đối số returnkhông nhất quán, nhập cụ thể và dễ bị lỗi nếu bạn không sử dụng 1 hoặc 0. Và như các nhận xét trước đây đã nêu, sử dụng 1 hoặc 0 ở đây không phải là cách phù hợp để tiếp cận chức năng này.

#!/bin/bash

function test_for_cat {
    if [ $1 = "cat" ];
    then
        true
        return
    else
        false
        return
    fi
}

for i in cat hat;
do
    echo "${i}:"
    if test_for_cat "${i}";
    then
        echo "- True"
    else
        echo "- False"
    fi
done

Đầu ra:

$ bash bash_return.sh

cat:
- True
hat:
- False

1

Nó có thể hoạt động nếu bạn viết lại function myfun(){ ... return 0; else return 1; fi;}như thế này function myfun(){ ... return; else false; fi;}. Đó là nếu falselà lệnh cuối cùng trong hàm bạn nhận được kết quả sai cho toàn bộ hàm nhưng returnlàm gián đoạn hàm với kết quả đúng. Tôi tin rằng điều đó đúng với người phiên dịch bash của tôi ít nhất.


1

Tôi tìm thấy hình thức ngắn nhất để kiểm tra đầu ra chức năng chỉ đơn giản là

do_something() {
    [[ -e $1 ]] # e.g. test file exists
}

do_something "myfile.txt" || { echo "File doesn't exist!"; exit 1; }

1

Vì lý do dễ đọc mã, tôi tin rằng trả về true / false nên:

  • ở trên một dòng
  • là một lệnh
  • dễ nhớ
  • đề cập đến từ khóa returntheo sau bởi một từ khóa khác ( truehoặc false)

Giải pháp của tôi là return $(true)hoặc return $(false)như được hiển thị:

is_directory()
{
    if [ -d "${1}" ]; then
        return $(true)
    else
        return $(false)
    fi
}

0

Theo dõi trên @Bruno Bronosky và @mrteatime, tôi đưa ra gợi ý rằng bạn chỉ cần viết trở lại boolean của bạn "ngược". Đó là thứ tôi nghĩ:

foo()
{
    if [ "$1" == "bar" ]; then
        true; return
    else
        false; return
    fi;
}

Điều đó loại bỏ yêu cầu hai dòng xấu xí cho mỗi câu lệnh return.

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.