การส่งคืนบูลีนจากฟังก์ชัน Bash


211

ฉันต้องการเขียนฟังก์ชั่นทุบตีที่ตรวจสอบว่าไฟล์มีคุณสมบัติบางอย่างและส่งกลับจริงหรือเท็จ จากนั้นฉันสามารถใช้มันในสคริปต์ของฉันใน "ถ้า" แต่สิ่งที่ฉันควรกลับมา?

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

จากนั้นฉันใช้มันเช่นนี้

if myfun filename.txt; then ...

แน่นอนว่ามันไม่ได้ผล สิ่งนี้จะสำเร็จได้อย่างไร


3
วางfunctionคำหลัก, myfun() {...}พอเพียง
glenn jackman

2
สิ่งที่สำคัญที่จะifเป็นสถานะที่ศูนย์ทางออกของmyfun: ถ้าmyfunออกด้วย0, then ...จะถูกดำเนินการ; ถ้ามันเป็นอย่างอื่น else ...จะถูกดำเนินการ
Eelvex

7
@nhed: functionคำหลักคือ bashism และจะทำให้เกิดข้อผิดพลาดทางไวยากรณ์ในเชลล์อื่น ๆ โดยพื้นฐานแล้วมันไม่จำเป็นหรือถูกห้ามดังนั้นทำไมต้องใช้มัน? มันไม่ได้มีประโยชน์แม้แต่กับเป้าหมาย grep เนื่องจากอาจไม่มี (grep ใช้()แทน)
Gordon Davisson

3
@GordonDavisson: อะไรนะ มีเปลือกหอยอื่นไหม ;-)
nhed

โปรดอย่าใช้ 0 และ 1 ดูstackoverflow.com/a/43840545/117471
Bruno Bronosky

คำตอบ:


333

ใช้ 0 สำหรับจริงและ 1 สำหรับเท็จ

ตัวอย่าง:

#!/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

แก้ไข

จากความคิดเห็นของ @ amichair สิ่งเหล่านี้ก็เป็นไปได้เช่นกัน

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


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

4
ไม่คุณไม่จำเป็นต้องทำเช่นนั้น - ดูตัวอย่าง
Erik

46
เพื่อความสามารถในการอ่านที่ดีขึ้นคุณสามารถใช้คำสั่ง 'true' (ซึ่งไม่ทำอะไรเลยและเสร็จสิ้นอย่างสมบูรณ์เช่นส่งคืน 0) และคำสั่ง 'false' (ซึ่งไม่ทำอะไรเลยและดำเนินการไม่สำเร็จ นอกจากนี้ยังมีฟังก์ชั่นที่สิ้นสุดโดยไม่ต้องมีคำสั่งกลับอย่างชัดเจนส่งกลับรหัสทางออกของคำสั่งดำเนินการที่ผ่านมาดังนั้นในตัวอย่างข้างต้น, [ -d "$1" ]ร่างกายของฟังก์ชั่นที่สามารถลดลงเหลือเพียง
amichair

24
Bengt: มันสมเหตุสมผลแล้วที่คุณคิดว่ามันเป็น "รหัสข้อผิดพลาด": รหัสข้อผิดพลาด 0 = ทุกอย่างก็โอเค = 0 ข้อผิดพลาด; รหัสข้อผิดพลาด 1 = สิ่งสำคัญที่การโทรนี้ควรทำล้มเหลว อื่น ๆ : ล้มเหลว! ค้นหาใน manpage
บินแกะ

7
มันทำให้รู้สึกเมื่อคุณพิจารณาว่าในการเขียนโปรแกรมสิ่งที่สามารถมักจะเพียงประสบความสำเร็จในทางเดียว แต่สามารถล้มเหลวในรูปแบบที่ไม่มีที่สิ้นสุด อาจจะไม่มีที่สิ้นสุด แต่ก็มีจำนวนมากอัตราต่อรองที่ซ้อนกันกับเรา สำเร็จ / ข้อผิดพลาดไม่ใช่บูลีน ฉันคิดว่านี่ "ใช้ 0 สำหรับจริงและ 1 สำหรับเท็จ" ควรอ่าน "ใช้ 0 เพื่อความสำเร็จและไม่เป็นศูนย์สำหรับความล้มเหลว"
Davos

6
โปรดอย่าใช้ 0 และ 1 ดูstackoverflow.com/a/43840545/117471
Bruno Bronosky

167

ทำไมคุณควรใส่ใจสิ่งที่ฉันพูดทั้งๆที่มีคำตอบ upvote 250+

มันไม่ได้ว่าและ0 = true 1 = falseมันเป็น: ศูนย์หมายความว่าไม่มีความล้มเหลว(ความสำเร็จ) และไม่ใช่ศูนย์หมายถึงความล้มเหลว(ประเภท N)

ในขณะที่คำตอบที่เลือกเป็นเทคนิค "true" กรุณาอย่าใส่return 1** ในรหัสของคุณสำหรับการเท็จ มันจะมีผลข้างเคียงที่โชคร้ายหลายอย่าง

  1. นักพัฒนาที่มีประสบการณ์จะมองคุณเป็นมือสมัครเล่น (ด้วยเหตุผลด้านล่าง)
  2. นักพัฒนาที่มีประสบการณ์จะไม่ทำสิ่งนี้ (ด้วยเหตุผลทั้งหมดด้านล่าง)
  3. มันเป็นข้อผิดพลาดได้ง่าย
    • แม้แต่นักพัฒนาที่มีประสบการณ์ก็สามารถเข้าใจผิดว่า 0 และ 1 ว่าผิดและจริงตามลำดับ (ด้วยเหตุผลข้างต้น)
  4. มันต้องการ (หรือจะสนับสนุน) ความคิดเห็นที่ไม่เกี่ยวข้องและไร้สาระ
  5. มีประโยชน์น้อยกว่าสถานะการส่งคืนโดยนัย

เรียนรู้ทุบตี

คู่มือทุบตีพูดว่า (ผมขอย้ำ)

คืน [n]

ทำให้ฟังก์ชันเชลล์หยุดการเรียกใช้งานและส่งกลับค่า n ไปยังผู้เรียก หากไม่ได้ระบุ nค่าส่งคืนคือสถานะออกของคำสั่งสุดท้ายที่ดำเนินการในฟังก์ชัน

ดังนั้นเราไม่จำเป็นต้องใช้ 0 และ 1 เพื่อระบุจริงและเท็จ ความจริงที่ว่าพวกเขาทำเช่นนั้นเป็นความรู้เล็กน้อยที่เป็นประโยชน์มีประโยชน์เฉพาะสำหรับการดีบักรหัสคำถามสัมภาษณ์และเป่าใจของมือใหม่

คู่มือทุบตียังบอกว่า

มิฉะนั้นสถานะการส่งคืนของฟังก์ชันคือสถานะออกของคำสั่งสุดท้ายที่ดำเนินการ

คู่มือทุบตียังบอกว่า

( $? ) จะขยายออกจากสถานะของท่อเบื้องหน้าส่วนใหญ่ดำเนินการเมื่อเร็ว ๆ นี้

เดี๋ยวก่อนรอ ท่อส่ง? ลองหันไปใช้คู่มือทุบตีอีกครั้ง

ไปป์ไลน์เป็นลำดับของคำสั่งอย่างน้อยหนึ่งคำคั่นด้วยหนึ่งในตัวดำเนินการควบคุม '|' หรือ '| &'

ใช่. พวกเขากล่าวว่า 1 คำสั่งเป็นไปป์ไลน์ ดังนั้นคำพูดทั้งสามคำพูดแบบเดียวกัน

  • $? บอกสิ่งที่เกิดขึ้นล่าสุด
  • มันฟองขึ้น

คำตอบของฉัน

ดังนั้นในขณะที่@Kambus แสดงให้เห็นว่าด้วยฟังก์ชั่นที่เรียบง่ายเช่นนี้ไม่returnจำเป็นเลย ฉันคิดว่ามันเรียบง่ายไม่สมจริงเมื่อเทียบกับความต้องการของคนส่วนใหญ่ที่จะอ่านสิ่งนี้

ทำไมreturn?

หากฟังก์ชั่นกำลังจะกลับสถานะทางออกของคำสั่งสุดท้ายทำไมใช้returnเลย? เพราะมันทำให้ฟังก์ชั่นหยุดการทำงาน

หยุดการทำงานภายใต้เงื่อนไขหลายข้อ

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

สิ่งที่เรามีที่นี่คือ ...

บรรทัด04คือการคืนค่า [-ish] จริงเนื่องจาก RHS ของ&&จะถูกเรียกใช้งานถ้า LHS เป็นจริงเท่านั้น

Line 09ส่งคืนทั้งการจับคู่สถานะของสายจริงหรือเท็จ08

Line 13ส่งคืน false เนื่องจากบรรทัด12

(ใช่สิ่งนี้สามารถเล่นกอล์ฟได้ แต่ตัวอย่างทั้งหมดถูกวางแผนไว้)

อีกรูปแบบทั่วไป

# 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

แจ้งให้ทราบว่าการตั้งค่าstatusตัวแปร demystifies $?ความหมายของ (แน่นอนคุณรู้ว่าสิ่งที่$?หมายถึง แต่บางคนมีความรู้น้อยกว่าที่คุณจะต้อง Google มันบางวันยกเว้นว่ารหัสของคุณกำลังทำการซื้อขายที่ความถี่สูงแสดงความรักตั้งค่าตัวแปร) แต่ที่จริงเอาไปคือ "ถ้า ไม่มีสถานะ "หรือตรงกันข้าม" หากสถานะออก "สามารถอ่านออกเสียงได้และอธิบายความหมาย อย่างไรก็ตามสิ่งสุดท้ายอาจมีความทะเยอทะยานเล็กน้อยเพราะการเห็นคำexitนั้นอาจทำให้คุณคิดว่ามันกำลังออกจากสคริปต์เมื่อในความเป็นจริงมันกำลังออกจาก$(...)subshell


** หากคุณยืนยันการใช้return 1งานที่ผิดพลาดฉันขอแนะนำให้คุณใช้อย่างน้อยreturn 255แทน สิ่งนี้จะทำให้ตัวคุณเองในอนาคตหรือผู้พัฒนารายอื่นที่ต้องรักษารหัสของคุณเพื่อตั้งคำถามว่า "เพราะเหตุใดจึงเป็น 255" อย่างน้อยพวกเขาก็จะให้ความสนใจและมีโอกาสที่ดีกว่าในการหลีกเลี่ยงข้อผิดพลาด


1
@ ZeroPhase 1 & 0 สำหรับเท็จ & จริงจะไร้สาระ หากคุณมีประเภทข้อมูลไบนารีไม่มีเหตุผลสำหรับสิ่งนั้น สิ่งที่คุณกำลังติดต่อด้วยใน bash คือรหัสสถานะที่สะท้อนถึงความสำเร็จ (เอกพจน์) และความล้มเหลว (พหูพจน์) มันคือ " ifความสำเร็จทำสิ่งนี้elseทำอย่างนั้น" ประสบความสำเร็จในสิ่งที่? จะได้รับการตรวจสอบจริง / เท็จจะได้รับการตรวจสอบสตริงจำนวนเต็มแฟ้ม, สมุด, สิทธิ์ในการเขียน, glob, regex, grep หรือคำสั่งอื่น ๆ ที่อยู่ภายใต้ความล้มเหลว
Bruno Bronosky

3
การหลีกเลี่ยงการใช้มาตรฐาน 0/1 เป็นค่าตอบแทนเพียงเพราะมันป้านและมีแนวโน้มที่จะสับสนเป็นเรื่องโง่ ภาษาของเชลล์ทั้งหมดเป็นป้านและมีแนวโน้มที่จะเกิดความสับสน Bash ตัวเองใช้การประชุม 0/1 = จริง / เท็จในตัวเองtrueและfalseคำสั่ง นั่นคือคำสำคัญtrueจะประเมินเป็นรหัสสถานะ 0 นอกจากนี้คำแถลง if-then ตามธรรมชาติดำเนินการกับ booleans ไม่ใช่รหัสความสำเร็จ หากมีข้อผิดพลาดเกิดขึ้นในระหว่างการดำเนินการบูลีนมันไม่ควรกลับจริงหรือเท็จ แต่เพียงทำลายการทำงาน มิฉะนั้นคุณจะได้รับผลบวกปลอม (pun)
Beejor

1
"ถ้า! $ (ออกจากสถานะ $) จากนั้น" - นั่นควรจะเป็นคำอธิบายที่ดีกว่า มันไม่ง่ายเลย ฉันต้องคิดว่าโปรแกรมจะออกก่อนที่จะพิมพ์ข้อความหรือไม่ คำตอบที่เหลือของคุณดี แต่มันทำให้เสีย
เครกฮิกส์

1
@BrunoBronosky ฉันอ่านคำตอบของคุณและฉันคิดว่าฉันเข้าใจความแตกต่างระหว่างไปป์ไลน์เนื้อหา (stdin-> stdout) และรหัสข้อผิดพลาดในค่าส่งคืน แต่ฉันไม่เข้าใจว่าทำไมreturn 1ควรหลีกเลี่ยงการใช้ สมมติว่าฉันมีvalidateฟังก์ชั่นฉันพบว่ามันสมเหตุสมผลreturn 1ถ้าการตรวจสอบล้มเหลว ท้ายที่สุดนั่นคือเหตุผลที่จะหยุดการทำงานของสคริปต์ถ้ามันไม่ได้จัดการอย่างถูกต้อง (เช่นเมื่อใช้set -e)
JepZ

1
@ Jepz นั่นเป็นคำถามที่ดี คุณถูกต้องว่าreturn 1ถูกต้อง ความกังวลของฉันคือความคิดเห็นทั้งหมดที่นี่โดยบอกว่า“ 0 = จริง 1 = เท็จคือ [แทรกคำเชิงลบ]” คนเหล่านั้นมีแนวโน้มที่จะอ่านรหัสของคุณในบางวัน ดังนั้นสำหรับพวกเขาที่เห็นreturn 255(หรือ 42 หรือ 2) ควรช่วยให้พวกเขาคิดเกี่ยวกับมันมากขึ้นและไม่ผิดพลาดว่าเป็น "จริง" set -eจะยังคงจับมัน
Bruno Bronosky

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

1
แล้วการปฏิเสธล่ะ
einpoklum

แล้ว @einpoklum ล่ะ?
Mark Reed

@ MarkReed: ฉันหมายถึงเพิ่มเคส "else" ในตัวอย่างของคุณ
einpoklum

myfun "path" || echo no
Hrobky

13

ระวังเมื่อทำการตรวจสอบไดเรกทอรีด้วยตัวเลือก -d!
ถ้าตัวแปร $ 1 ว่างเปล่าเช็คจะยังคงประสบความสำเร็จ ตรวจสอบให้แน่ใจว่าตัวแปรไม่ว่างเปล่า

#! /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
ฉันไม่แน่ใจว่าคำถามนี้ตอบคำถามอย่างไร ในขณะที่มันเป็นเรื่องดีที่รู้ว่า $ 1 ที่ว่างเปล่าสามารถคืนค่าจริงเมื่อว่างเปล่า แต่ก็ไม่ได้ให้ข้อมูลเชิงลึกเกี่ยวกับวิธีการส่งกลับจริงหรือเท็จจากฟังก์ชั่นทุบตี ฉันขอแนะนำให้สร้างคำถามใหม่ "จะเกิดอะไรขึ้นเมื่อคุณทำการทดสอบตัวแปรเชลล์ที่ว่างเปล่า" จากนั้นโพสต์สิ่งนี้เป็นคำตอบ
DRaehal

3
โปรดทราบว่าหากคุณเพิ่มการอ้างอิงที่เหมาะสมใน$1( "$1") คุณไม่จำเป็นต้องตรวจสอบตัวแปรว่าง [[ -d "$1" ]]จะล้มเหลวเพราะ""ไม่ได้เป็นไดเรกทอรี
morgents

3

ฉันพบจุด (ยังไม่ได้อธิบายอย่างชัดเจน?) ซึ่งฉันสะดุด นั่นคือไม่ใช่วิธีคืนบูลีน แต่จะประเมินได้อย่างไร!

ฉันพยายามจะพูดif [ myfunc ]; then ...แต่ก็ผิดปกติ คุณต้องไม่ใช้วงเล็บ! if myfunc; then ...เป็นวิธีที่จะทำ

ณ ที่ @Bruno และคนอื่น ๆ ย้ำtrueและfalseเป็นคำสั่งไม่ใช่ค่า! มันสำคัญมากที่จะเข้าใจบูลีนในเชลล์สคริปต์

ในบทความนี้ผมอธิบายและสาธิตการใช้บูลตัวแปร : https://stackoverflow.com/a/55174008/3220983 ฉันขอแนะนำให้ตรวจสอบเพราะมันเกี่ยวข้องอย่างใกล้ชิด

ที่นี่ฉันจะให้ตัวอย่างของการย้อนกลับและการประเมินบูลีนจากฟังก์ชั่น:

นี้:

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

ไม่มีเอาต์พุต echo (เช่นfalse ส่งคืนค่าเท็จ)

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

ผลิต:

it is                                                        

(เช่นtrue ผลตอบแทนจริง)

และ

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

ผลิต:

it is                                                                           

เพราะ 0 (คือจริง) ก็กลับมาโดยปริยาย

นี่คือสิ่งที่ทำให้ฉันกลัว ...

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

ผลิต:

it is                                                                           

และ

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

ยังผลิต:

it is                                                                           

การใช้วงเล็บที่นี่ทำให้เกิดผลบวกปลอม ! (ฉันอนุมานผลลัพธ์คำสั่ง "outer" คือ 0)

สิ่งสำคัญที่ต้องหลีกเลี่ยงจากการโพสต์ของฉันคือ: อย่าใช้วงเล็บเพื่อประเมินฟังก์ชั่นบูลีน (หรือตัวแปร) อย่างที่คุณต้องการสำหรับการตรวจสอบความเท่าเทียมกันทั่วไปif [ x -eq 1 ]; then...!


2

ใช้คำสั่งtrueหรือfalseคำสั่งก่อนหน้าของคุณreturnจากนั้นreturnไม่มีพารามิเตอร์ returnโดยอัตโนมัติจะใช้ค่าของคำสั่งสุดท้ายของคุณ

การระบุอาร์กิวเมนต์ให้returnไม่สอดคล้องกันพิมพ์เฉพาะและมีแนวโน้มที่จะเกิดข้อผิดพลาดหากคุณไม่ได้ใช้ 1 หรือ 0 และเนื่องจากความคิดเห็นก่อนหน้านี้ระบุไว้ว่าการใช้ 1 หรือ 0 ที่นี่ไม่ใช่วิธีที่เหมาะสมในการเข้าถึงฟังก์ชันนี้

#!/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

เอาท์พุท:

$ bash bash_return.sh

cat:
- True
hat:
- False

1

อาจใช้งานได้หากคุณเขียน ซ้ำfunction myfun(){ ... return 0; else return 1; fi;}เช่นนี้ function myfun(){ ... return; else false; fi;}นั่นคือถ้าfalseเป็นคำสั่งสุดท้ายในฟังก์ชั่นที่คุณได้รับผลลัพธ์ที่ผิดพลาดสำหรับฟังก์ชั่นทั้งหมด แต่returnขัดจังหวะฟังก์ชั่นที่มีผลลัพธ์ที่แท้จริงอยู่แล้ว ฉันเชื่อว่ามันเป็นจริงสำหรับล่ามทุบตีของฉันอย่างน้อย


1

ฉันพบว่ารูปแบบที่สั้นที่สุดเพื่อทดสอบฟังก์ชั่นการส่งออกเป็นเพียง

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

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

1

สำหรับเหตุผลในการอ่านรหัสฉันเชื่อว่าการส่งคืนจริง / เท็จควร:

  • เป็นหนึ่งบรรทัด
  • เป็นหนึ่งคำสั่ง
  • จำง่าย
  • พูดถึงคำหลักreturnตามด้วยคำหลักอื่น ( trueหรือfalse)

ทางออกของฉันคือreturn $(true)หรือreturn $(false)ตามที่ปรากฏ:

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

0

การติดตาม @Bruno Bronosky และ @mrteatime ฉันขอเสนอคำแนะนำที่คุณเพิ่งเขียนบูลีนรีเทิร์น "ถอยหลัง" นี่คือสิ่งที่ฉันหมายถึง:

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

ที่กำจัดความต้องการสองบรรทัดที่น่าเกลียดสำหรับทุกคำสั่ง return

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.