Sự khác biệt giữa trả về và thoát trong các hàm Bash


429

Sự khác biệt giữa câu lệnh returnexitcâu lệnh trong hàm Bash đối với mã thoát là gì?


55
Protip: nhập help <command>vào shell của bạn để có được thông tin về những gì shellin sẽ làm. Trong trường hợp của bạn help returnhelp exit
SiegeX

Câu trả lời:


318

Từ man bashtrên return [n];

Làm cho một hàm dừng thực thi và trả về giá trị được chỉ định bởi n cho người gọi nó. Nếu n bị bỏ qua, trạng thái trả về là trạng thái của lệnh cuối cùng được thực thi trong thân hàm.

... trên exit [n]:

Làm cho vỏ thoát ra với trạng thái n. Nếu n bị bỏ qua, trạng thái thoát là trạng thái của lệnh cuối cùng được thực thi. Một cái bẫy trên EXIT được thực thi trước khi shell kết thúc.

BIÊN TẬP:

Theo chỉnh sửa câu hỏi của bạn, liên quan đến mã thoát, returnkhông liên quan gì đến mã thoát. Mã thoát được dành cho các ứng dụng / tập lệnh , không phải chức năng. Vì vậy, về vấn đề này, từ khóa duy nhất đặt mã thoát của tập lệnh (từ khóa có thể bị bắt bởi chương trình gọi bằng $?biến shell) là exit.

EDIT 2:

Tuyên bố cuối cùng của tôi đề cập đến exitlà gây ra một số ý kiến. Nó được tạo ra để phân biệt returnexittheo cách hiểu của OP, và trên thực tế, tại bất kỳ điểm nào của tập lệnh chương trình / shell, exitlà cách duy nhất để kết thúc tập lệnh với mã thoát cho quy trình gọi.

Mỗi lệnh thực hiện trong vỏ tạo ra một "mã thoát" địa phương: nó đặt $?biến để mã đó, và có thể được sử dụng với if, &&và các nhà khai thác khác để có điều kiện thực hiện các lệnh khác.

Các mã thoát này (và giá trị của $?biến) được đặt lại theo từng lệnh thực thi.

Ngẫu nhiên, mã thoát của lệnh cuối cùng được thực thi bởi tập lệnh được sử dụng làm mã thoát của chính tập lệnh mà quá trình gọi.

Cuối cùng, các hàm, khi được gọi, hoạt động như các lệnh shell đối với các mã thoát. Mã thoát của hàm ( trong hàm) được đặt bằng cách sử dụng return. Vì vậy, khi trong một chức năng return 0được chạy, việc thực thi chức năng chấm dứt, đưa ra mã thoát là 0.


10
Không chính xác. Nó luôn trả về một giá trị từ shell hiện tại. Không quan trọng bạn có ở trong chức năng hay không.
Diego Sevilla

10
Nhận xét về chỉnh sửa của bạn: Tôi có thể nhầm lẫn giữa các giá trị trả về và mã thoát, nhưng func(){ return 50; };func;echo $?lặp lại 50. Vì vậy, $?biến shell dường như không bị giới hạn exit.
lecodesportif

8
" $?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." Lối ra đó có thể từ shell dưới dạng một cuộc gọi đến exit(hoặc nhấn vào cuối tập lệnh) hoặc ở dạng một cuộc gọi đến returntrong một chức năng.
Cuộc bao vây

11
@lecodesportif: $? Quá trình / tập lệnh hiện tại bị giới hạn ở exithoặc kết quả của lệnh cuối cùng được thực thi bởi tập lệnh này. Vì vậy, nếu dòng script cuối cùng của bạn là lệnh gọi hàm đó và hàm đó trả về 50, vâng, cái $?mà bạn tạo ra cho quá trình gọi bạn là 50. Tuy nhiên, điều đó không phải làm với return, bởi vì đây là giới hạn trong kịch bản hiện tại. Nó chỉ xảy ra được trả về nếu lệnh gọi hàm này là câu cuối cùng của tập lệnh. exittuy nhiên, luôn hoàn thành tập lệnh và trả lại giá trị đó $? cho quy trình gọi .
Diego Sevilla

11
-1 vì đã gây nhầm lẫn cho tôi với dòng " returnkhông liên quan gì đến mã thoát". Thử nghiệm cho tôi biết rằng không có sự khác biệt về chức năng giữa mã trả về của hàm và mã thoát của tập lệnh.
Jack

296

returnsẽ làm cho hàm hiện tại đi ra khỏi phạm vi, trong khi đó exitsẽ khiến tập lệnh kết thúc tại điểm được gọi. Đây là một chương trình mẫu để giúp giải thích điều này:

#!/bin/bash

retfunc()
{
    echo "this is retfunc()"
    return 1
}

exitfunc()
{
    echo "this is exitfunc()"
    exit 1
}

retfunc
echo "We are still here"
exitfunc
echo "We will never see this"

Đầu ra

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()

17
Ví dụ tốt đẹp. Bạn cũng có thể hiển thị giá trị thoát của 1 in $?.
Diego Sevilla

48
Lưu ý rằng chức năng này sẽ KHÔNG in "Chúng tôi vẫn ở đây" nếu bạn thêm "set -e" trước khi gọi đến "retfunc".
Michael

4
Tuy nhiên, echo fnord | while read x; do exitfunc; done; echo "still here"sẽ in "vẫn ở đây". Có vẻ như chỉ có whilelớp vỏ phụ được thoát ra trong kịch bản này.
tripleee

Một cách giải quyết gần đúng là done || exit $?nhưng điều đó là xấu xí và không chính xác tương đương.
tripleee

2
+1 Có thể hữu ích khi thêm: `` ` returnsẽ khiến hàm hiện tại hoặc tập lệnh có nguồn gốc đi ra khỏi phạm vi```.
Isaac

56

Tôi không nghĩ có ai thực sự trả lời đầy đủ câu hỏi vì họ không mô tả cách hai người được sử dụng. OK Tôi nghĩ rằng chúng ta biết rằng lối thoát sẽ giết chết tập lệnh, bất cứ khi nào nó được gọi và bạn có thể gán trạng thái cho nó cũng như thoát hoặc thoát 0 hoặc thoát 7, v.v. Điều này có thể được sử dụng để xác định cách tập lệnh bị buộc dừng lại nếu được gọi bởi tập lệnh khác, vv Đủ khi thoát.

return khi được gọi sẽ trả về giá trị được chỉ định để biểu thị hành vi của hàm, thường là 1 hoặc 0. Ví dụ:

    #!/bin/bash
    isdirectory() {
      if [ -d "$1" ]
      then
        return 0
      else
        return 1
      fi
    echo "you will not see anything after the return like this text"
    }

kiểm tra như thế này:

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

hoặc như thế này:

    isdirectory || echo "not a directory"

Trong ví dụ này, kiểm tra có thể được sử dụng để chỉ ra nếu thư mục được tìm thấy. lưu ý rằng bất cứ điều gì sau khi trả về sẽ không được thực thi trong hàm. 0 là đúng nhưng sai là 1 trong vỏ, khác với các lang prog khác.

Để biết thêm thông tin về các chức năng: http://www.linuxjournal.com/content/return-values-bash-fifts

LƯU Ý: Hàm isdirectory chỉ dành cho mục đích hướng dẫn. Đây không phải là cách bạn thực hiện một tùy chọn như vậy trong một kịch bản thực sự.


3
Hoặc chỉ sử dụng test -d $1để đạt được kết quả tương tự. Không bao giờ làm if <check> return else return. <check>một mình sẽ làm điều tương tự trong tất cả các ngôn ngữ mà tôi biết ít nhất.
erikbwork

4
Để rõ ràng hơn về những gì erik đang nói: isdirectory() { [ -d "$1" ]; }sẽ hành xử chính xác giống như những gì bạn có ở đây: Giá trị trả về mặc định của hàm shell, cho dù bằng cách đạt đến cuối mã của nó hoặc bởi returnkhông có đối số, là của lệnh gần đây nhất.
Charles Duffy

11
Các nhà bình luận khác ở đây đang chỉ trích phong cách của ví dụ của Mike Q, khi thực sự anh ta đang nói về hành vi của returntuyên bố. Đúng là ví dụ của ông rất đơn giản và không được sử dụng trong sản xuất. Nhưng nó đơn giản, vì vậy nó hoàn thành tốt nhiệm vụ của mình. Không có gì sai với nó.
Mike S

Cảm ơn Mike S, vâng tôi đồng ý rằng ví dụ đơn giản nhất giải thích tốt nhất về thoát so với trả về. Các ý kiến ​​khác chắc chắn là hợp lệ và nên được xem xét cho các lập trình viên bash tiên tiến hơn ;-)
Mike Q

1
Một số người có thể nói rằng nó không liên quan chính xác đến câu hỏi, nhưng nó liên quan và trả lời vấn đề tôi gặp phải khiến tôi tìm thấy Câu hỏi này. :-)
Jesse Steele

33

Hãy nhớ rằng, các hàm là nội bộ của một tập lệnh và thường trả về từ khi chúng được gọi bằng cách sử dụng câu lệnh return. Gọi một tập lệnh bên ngoài hoàn toàn là một vấn đề khác và các tập lệnh thường kết thúc bằng một câu lệnh thoát.

Sự khác biệt "giữa câu lệnh return và exit trong các hàm BASH liên quan đến mã thoát" là rất ít. Cả hai trả về một trạng thái, không phải giá trị mỗi se. Trạng thái bằng 0 biểu thị thành công, trong khi bất kỳ trạng thái nào khác (1 đến 255) biểu thị sự thất bại. Câu lệnh return sẽ quay trở lại tập lệnh từ nơi nó được gọi, trong khi câu lệnh thoát sẽ kết thúc toàn bộ tập lệnh từ bất kỳ nơi nào nó gặp phải.

return 0  # returns to where the function was called.  $? contains 0 (success).

return 1  # returns to where the function was called.  $? contains 1 (failure).

exit 0  # exits the script completely.  $? contains 0 (success).

exit 1  # exits the script completely.  $? contains 1 (failure).

Nếu chức năng của bạn đơn giản kết thúc mà không có câu lệnh return, trạng thái của lệnh cuối cùng được thực thi sẽ được trả về dưới dạng mã trạng thái (và sẽ được đặt vào $?).

Hãy nhớ rằng, trả lại và thoát trả lại mã trạng thái từ 0 đến 255, có sẵn trong $?. Bạn không thể nhét bất cứ thứ gì khác vào mã trạng thái (ví dụ: trả về "cat"); nó sẽ không làm việc. Nhưng, một tập lệnh có thể trả lại 255 lý do khác nhau cho sự thất bại bằng cách sử dụng mã trạng thái.

Bạn có thể đặt các biến có trong tập lệnh gọi hoặc kết quả echo trong hàm và sử dụng thay thế lệnh trong tập lệnh gọi; nhưng mục đích của việc trả lại và thoát là để truyền mã trạng thái, không phải giá trị hoặc kết quả tính toán như người ta có thể mong đợi trong một ngôn ngữ lập trình như C.


25

Đôi khi, bạn chạy một kịch bản bằng cách sử dụng .hoặc source.

. a.sh

Nếu bạn bao gồm một exittrong a.sh, nó sẽ không chỉ chấm dứt tập lệnh, mà kết thúc phiên shell của bạn.

Nếu bạn bao gồm một returntrong a.sh, nó chỉ đơn giản dừng xử lý tập lệnh.


1
Nhưng khi tôi chạy a.sh, tôi gặp lỗi return: can only 'return' from a function or sourced script, điều này làm cho nó không phù hợp với tập lệnh chung.
Peter - Tái lập Monica

Ở cấp độ cao nhất trong một kịch bản, không phù hợp trong các alltình huống. Sử dụng .hoặc sourcechạy tập lệnh trong trình bao hiện tại, thay vì sinh ra một trình bao phụ. Kịch bản phải biết nó được sử dụng như thế nào. Khốn cho người dùng làm điều đó ngược lại. Cá nhân, tôi khuyên bạn nên đọc kịch bản trước khi chạy chúng lần đầu tiên.
Jesse Chisholm

3
Một mẹo tuyệt vời tôi đi qua là sử dụng một trapchức năng cho ERR EXITvà sau đó đầu tiên tiết kiệm mã lối ra của một lệnh thất bại errCode=$?và sau đó thoát khỏi kịch bản (có nguồn gốc hay không) với return $errCode || exit $errCodenơi ||có nghĩa là "nếu tôi không thể trở về vì tôi đã không có nguồn gốc , chỉ cần thoát ra thay thế ".
dragon788

6

Nói một cách đơn giản (chủ yếu dành cho người mới viết mã), chúng ta có thể nói,

`return` : exits the function,
`exit()` : exits the program(called as process while running)

Ngoài ra Nếu bạn quan sát, điều này rất cơ bản nhưng ...,

`return` : is the keyword
`exit()` : is the function

1
Trong một tập lệnh bash, exitkhông có nhiều hơn hoặc ít hơn một chức năng hơn return. Chúng là các lệnh tích hợp. Chúng thậm chí không phải là từ dành riêng.
Peter - Tái lập Monica

6
  • exitchấm dứt quá trình hiện tại ; có hoặc không có mã thoát, hãy coi đây là một hệ thống nhiều hơn một chức năng của chương trình. Lưu ý rằng khi tìm nguồn cung ứng, exitsẽ kết thúc shell, tuy nhiên, khi chạy sẽ chỉ exitlà tập lệnh.

  • returntừ một chức năng quay trở lại hướng dẫn sau cuộc gọi, có hoặc không có mã trả về. returnlà tùy chọn và nó ẩn ở cuối hàm. returnchỉ có thể được sử dụng bên trong một chức năng.

Tôi muốn thêm rằng trong khi có nguồn gốc, không dễ để exittập lệnh từ bên trong một hàm mà không làm chết trình bao. Tôi nghĩ rằng, một ví dụ tốt hơn trong kịch bản 'kiểm tra'

#!/bin/bash
function die(){
   echo ${1:=Something terrible wrong happen}
   #... clean your trash
   exit 1
}

[ -f /whatever/ ] || die "whatever is not available"
# now we can proceed
echo "continue"

làm như sau:

user$ ./test
Whatever is not available
user$

test -và vỏ sẽ đóng lại.

user$ . ./test
Whatever is not available

chỉ testsẽ kết thúc và lời nhắc sẽ hiển thị.

Giải pháp là bao gồm các thủ tục có khả năng trong ()

#!/bin/bash
function die(){
   echo $(1:=Something terrible wrong happen)
   #... clean your trash
   exit 1
}

( # added        
    [ -f /whatever/ ] || die "whatever is not available"
    # now we can proceed
    echo "continue"
) # added

Bây giờ, trong cả hai trường hợp testsẽ chỉ thoát.


Việc thêm ()đặt khối đó vào lớp vỏ phụ, thực hiện không thực hiện lệnh .(nguồn) như thể bạn đã chạy tập lệnh kiểm tra bình thường, nằm trong lớp vỏ phụ. IOf script không được chạy với .hoặc sourcesau đó bạn thực sự có 2 shell con.
Jesse Chisholm

3

Câu hỏi của OP: Sự khác biệt giữa câu lệnh return và exit trong các hàm BASH đối với mã thoát là gì?

Firtly, một số làm rõ là cần thiết:

  • Một câu lệnh (return | exit) là không cần thiết để chấm dứt thực thi một (hàm | shell). Một (hàm | shell) sẽ chấm dứt khi đến cuối danh sách mã của nó, ngay cả khi không có câu lệnh (return | exit).
  • Không cần phải có câu lệnh (return | exit) để truyền lại giá trị từ hàm đã kết thúc (hàm | shell). Mỗi quá trình có một biến tích hợp $? mà luôn luôn có một giá trị số. Đó là một biến đặc biệt không thể được đặt như "? = 1", nhưng chỉ được đặt theo những cách đặc biệt (xem bên dưới *). Giá trị của $? sau lệnh cuối cùng được thực thi trong (được gọi là hàm | vỏ phụ) là giá trị được truyền lại cho (hàm gọi hàm | vỏ cha). Điều đó đúng cho dù lệnh cuối cùng được thực thi là ("return [n]" | "exit [n]") hay plain ("return" hoặc thứ gì khác xảy ra là lệnh cuối cùng trong mã hàm được gọi.

Trong danh sách dấu đầu dòng ở trên, chọn từ "(x | y)" luôn là mục đầu tiên hoặc luôn là mục thứ hai để nhận các câu lệnh về hàm & return hoặc shell & exit tương ứng.

Điều rõ ràng là cả hai đều chia sẻ cách sử dụng chung của biến đặc biệt $? để vượt qua các giá trị trở lên sau khi chúng chấm dứt.

* Bây giờ cho những cách đặc biệt mà $? có thể thiết lập:

  • Khi một hàm được gọi chấm dứt và trả về cho người gọi thì $? trong người gọi sẽ bằng giá trị cuối cùng của $? trong hàm kết thúc.
  • Khi một vỏ cha mẹ chờ đợi một cách rõ ràng hoặc rõ ràng trên một vỏ con duy nhất và được giải phóng bằng cách chấm dứt lớp vỏ con đó, thì $? trong shell cha sẽ bằng giá trị cuối cùng của $? trong vỏ phụ chấm dứt.
  • Một số hàm dựng sẵn có thể sửa đổi $? tùy thuộc vào kết quả của họ. Nhưng một số thì không.
  • Các hàm dựng sẵn "return" và "exit", khi theo sau là một đối số số cả $? với đối số, và chấm dứt thực hiện.

Điều đáng chú ý là $? có thể được gán một giá trị bằng cách gọi exit trong shell phụ, như thế này:

# (exit 259)
# echo $?
3  

4
Trong trường hợp một số bỏ lỡ nó, exit 259echos như 3vì giá trị xuất cảnh cuối cùng là một byte duy nhất. 259 % 256 = 3
Jesse Chisholm

0

Trước hết, returnlà một từ khóa và exitbạn tôi là một chức năng.

Điều đó nói rằng, đây là một cách giải thích đơn giản nhất.

return Nó trả về một giá trị từ một hàm.

exit Nó thoát ra hoặc từ bỏ lớp vỏ hiện tại.


Không hẳn vậy! Bạn đang sai về mặt logic. Thoát là một chức năng trong khi returnlà một từ khóa. Trả về không chỉ là mã thoát, đó là lý do tại sao việc so sánh không công bằng.
Ahmad Awais

Tôi đã chỉnh sửa nó để làm rõ hơn vấn đề mà tôi đang cố gắng thực hiện. Cảm ơn đã giúp tôi làm điều đó.
Ahmad Awais

4
Không exitphải returnlà "từ khóa", hoặc, như hướng dẫn bash gọi chúng là "từ dành riêng". Cả hai đều không phải là "hàm", theo nghĩa của hàm bash. Cả hai đều là lệnh dựng sẵn, trong bash lingo. (Có một chức năng thư viện C chuẩn gọi là exit(), và ngôn ngữ lập trình C có một từ dành riêng return, nhưng những người không nên nhầm lẫn với các lệnh bash, mặc dù ngữ nghĩa của họ là tò mò tương tự.)
Peter - Khôi phục Monica
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.