การส่งคืนค่าจากฟังก์ชันที่เรียกในเชลล์สคริปต์


126

ฉันต้องการส่งคืนค่าจากฟังก์ชันที่เรียกในเชลล์สคริปต์ บางทีฉันอาจไม่มีไวยากรณ์ ฉันลองใช้ตัวแปรส่วนกลาง แต่นั่นก็ไม่ได้ผล รหัสคือ:

lockdir="somedir"
test() {
    retval=""

    if mkdir "$lockdir"
        then    # Directory did not exist, but it was created successfully
            echo >&2 "successfully acquired lock: $lockdir"
            retval="true"
        else
            echo >&2 "cannot acquire lock, giving up on $lockdir"
            retval="false"
    fi
    return retval
}


retval=test()
if [ "$retval" == "true" ]
    then
        echo "directory not created"
    else
        echo "directory already created"
fi

ไม่เกี่ยวกับคำถามของคุณ แต่อย่างไรก็ตาม ... ถ้าคุณพยายามจะล็อคคุณอาจใช้คำสั่ง "lockfile"
Víctor Herraiz

คำตอบ:


277

ฟังก์ชัน Bash ไม่สามารถส่งคืนสตริงได้โดยตรงอย่างที่คุณต้องการ คุณสามารถทำสามสิ่ง:

  1. สะท้อนสตริง
  2. ส่งคืนสถานะการออกซึ่งเป็นตัวเลขไม่ใช่สตริง
  3. แชร์ตัวแปร

นี่เป็นเรื่องจริงสำหรับเปลือกหอยอื่น ๆ

วิธีดำเนินการแต่ละตัวเลือกมีดังนี้

1. สตริงเสียงสะท้อน

lockdir="somedir"
testlock(){
    retval=""
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval="true"
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval="false"
    fi
    echo "$retval"
}

retval=$( testlock )
if [ "$retval" == "true" ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2. กลับสถานะทางออก

lockdir="somedir"
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
    return "$retval"
}

testlock
retval=$?
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

3. แบ่งปันตัวแปร

lockdir="somedir"
retval=-1
testlock(){
    if mkdir "$lockdir"
    then # Directory did not exist, but it was created successfully
         echo >&2 "successfully acquired lock: $lockdir"
         retval=0
    else
         echo >&2 "cannot acquire lock, giving up on $lockdir"
         retval=1
    fi
}

testlock
if [ "$retval" == 0 ]
then
     echo "directory not created"
else
     echo "directory already created"
fi

2
อย่าใช้functionคีย์เวิร์ดเพื่อกำหนดฟังก์ชันทุบตี นั่นจะทำให้พกพาได้น้อยลง กำลังถอดมันออก
dimir

2
ในตัวอย่างที่สามของคุณ retval ไม่ใช่ตัวแปรสภาพแวดล้อม มันเป็นเพียงตัวแปรเชลล์ มันจะกลายเป็นตัวแปรสภาพแวดล้อมก็ต่อเมื่อคุณส่งออก บางทีชื่อของตัวอย่างที่สามควรเป็น "global variable" แทนที่จะเป็น "environment variable"
William Pursell

4
ในตัวอย่างที่สองแทนที่จะกำหนดจาก $? มันเป็นสำนวนมากกว่าที่จะเขียน "if testlock; then ... "
William Pursell

@WilliamPursell ฉันลบคำ 'สิ่งแวดล้อม' ผิด มาเก็บ "$?" เพื่อวัตถุประสงค์ในการสอน ฉันได้เปิดใช้งานชุมชน Wiki ดังนั้นคุณมีอิสระที่จะปรับปรุงคำตอบ ;-)
olibre

1
@ManuelJordan ฟังก์ชั่นสามารถส่งคืนรหัสทางออกและ> & 2 บันทึกเป็น stderror เท่านั้นดังนั้นเสียงสะท้อนสุดท้ายจึงถูกเขียนไปยัง stdout ดังนั้นฟังก์ชันการโทรจะจับเฉพาะ stdout เท่านั้นและไม่ใช่ stderr สมมติว่าการดำเนินการเป็นเธรดเดียวตัวเลือกที่ดีกว่าคือการรักษาตัวแปรที่กำหนดเองเฉพาะเช่น TEST_LOCK_STATUS = "" วิธีการภายนอกที่ทุกคนสามารถใช้หลังจากเรียก testlock และรีเซ็ตทุกครั้งเมื่อเริ่มต้นเมธอด
kisna

16

คุณทำงานหนักเกินไป สคริปต์ทั้งหมดของคุณควรเป็น:

if mkdir "$lockdir" 2> /dev/null; then 
  echo lock acquired
else
  echo could not acquire lock >&2
fi

แต่ถึงอย่างนั้นก็อาจจะเกินไป ฉันจะเขียนโค้ด:

mkdir "$lockdir" || exit 1

แต่ข้อความแสดงข้อผิดพลาดที่ได้นั้นค่อนข้างคลุมเครือ


1
ข้อความแสดงข้อผิดพลาดที่หายไปนั้นง่ายพอที่จะแก้ไขได้แม้ว่าจะดูละเอียดกว่าเล็กน้อย: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }(สังเกต;ก่อนวงเล็บปีกกาปิด) นอกจากนี้ผมมักจะกำหนดฟังก์ชั่นที่ใช้พารามิเตอร์ข้อความตัวเลือกที่จะพิมพ์เพื่อ stderr แล้วออกด้วยรหัส return 1 mkdir "$lockdir" || fail "could not create lock dir"ล้มเหลวช่วยให้ผมที่จะใช้อ่านได้มากขึ้น
blubberdiblub

@blubberdiblub: แต่ฟังก์ชัน fail ไม่สามารถออกจากฟังก์ชันหรือสคริปต์ "current" ได้หรือไม่ ดังนั้นคุณต้องใช้cmd || fail "error msg" || return 1หากคุณต้องการทำเช่นนั้นหรือไม่?
สูงสุด

@ สูงสุดไม่ใช่ฟังก์ชันปัจจุบันที่ถูกต้อง แต่มันจะออกจากสคริปต์ปัจจุบันตราบใดที่คุณเรียกมันว่าเป็นคำสั่งและไม่ได้มามัน ฉันมักจะคิดว่าfailฟังก์ชันดังกล่าวใช้สำหรับสถานการณ์ที่ร้ายแรงเท่านั้น
blubberdiblub

12

หากเป็นเพียงการทดสอบจริง / เท็จให้ทำหน้าที่ของคุณreturn 0เพื่อความสำเร็จและreturn 1สำหรับความล้มเหลว การทดสอบจะเป็น:

if function_name; then
  do something
else
  error condition
fi

สิ่งที่ฉันกำลังมองหา
ซามูเอล

มีวิธีใช้สัญกรณ์นี้สำหรับฟังก์ชันที่กำหนดพารามิเตอร์หรือไม่
อเล็กซ์

@alex ช่วยยกตัวอย่างความหมายของ "parameterized function" ได้ไหม
เกลนน์แจ็คแมน

"myCopyFunc $ {SOURCE} $ {DEST}" กลับ 0 เมื่อประสบความสำเร็จ เช่นในฉบับนี้stackoverflow.com/questions/6212219/…
Alex

ใช่มันสมบูรณ์ดี
Glenn Jackman

2

ฉันคิดว่าการคืนค่า 0 สำหรับ succ / 1 สำหรับความล้มเหลว (glenn jackman) และคำตอบที่ชัดเจนและอธิบายของ olibre กล่าวได้ทั้งหมด เพียงแค่พูดถึงวิธีการ "คำสั่งผสม" ประเภทหนึ่งสำหรับกรณีที่ผลลัพธ์ไม่ใช่ไบนารีและคุณต้องการตั้งค่าตัวแปรแทนที่จะ "สะท้อนออกมา" ผลลัพธ์ (ตัวอย่างเช่นหากฟังก์ชันของคุณเป็นเช่นกันหากสมมติว่าสะท้อนบางสิ่งวิธีนี้จะ ไม่ทำงาน). แล้วอะไรล่ะ? (ด้านล่างคือ Bourne Shell)

# Syntax _w (wrapReturn)
# arg1 : method to wrap
# arg2 : variable to set
_w(){
eval $1
read $2 <<EOF
$?
EOF
eval $2=\$$2
}

เช่นใน (ใช่ตัวอย่างค่อนข้างงี่เง่ามันเป็นเพียง .. ตัวอย่าง)

getDay(){
  d=`date '+%d'`
  [ $d -gt 255 ] && echo "Oh no a return value is 0-255!" && BAIL=0 # this will of course never happen, it's just to clarify the nature of returns
  return $d
}

dayzToSalary(){
  daysLeft=0
  if [ $1 -lt 26 ]; then 
      daysLeft=`expr 25 - $1`
  else
     lastDayInMonth=`date -d "`date +%Y%m01` +1 month -1 day" +%d`
     rest=`expr $lastDayInMonth - 25`
     daysLeft=`expr 25 + $rest`
  fi
  echo "Mate, it's another $daysLeft days.."
}

# main
_w getDay DAY # call getDay, save the result in the DAY variable
dayzToSalary $DAY

1

ในกรณีที่คุณมีพารามิเตอร์บางอย่างที่จะส่งผ่านไปยังฟังก์ชันและต้องการค่าตอบแทน ที่นี่ฉันกำลังส่ง "12345" เป็นอาร์กิวเมนต์ไปยังฟังก์ชันและหลังจากประมวลผลตัวแปร XYZ ที่ส่งคืนซึ่งจะกำหนดให้กับ VALUE

#!/bin/bash
getValue()
{
    ABC=$1
    XYZ="something"$ABC
    echo $XYZ
}


VALUE=$( getValue "12345" )
echo $VALUE

เอาท์พุท:

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