Trả về giá trị từ hàm được gọi trong tập lệnh shell


126

Tôi muốn trả về giá trị từ một hàm được gọi trong tập lệnh shell. Có lẽ tôi đang thiếu cú ​​pháp. Tôi đã thử sử dụng các biến toàn cục. Nhưng điều đó cũng không hoạt động. Mã là:

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

Không liên quan đến câu hỏi của bạn, nhưng dù sao thì ... nếu bạn đang cố lấy một ổ khóa, bạn có thể sử dụng lệnh "lockfile".
Víctor Herraiz

Câu trả lời:


277

Một hàm Bash không thể trả về một chuỗi trực tiếp như bạn muốn. Bạn có thể làm ba điều:

  1. Echo một chuỗi
  2. Trả về trạng thái thoát, là một số, không phải một chuỗi
  3. Chia sẻ một biến

Điều này cũng đúng với một số loại vỏ khác.

Dưới đây là cách thực hiện từng tùy chọn đó:

1. Chuỗi tiếng vang

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. Trở lại trạng thái thoát

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. Chia sẻ biến

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
Không sử dụng một functiontừ khóa để xác định một hàm bash. Điều đó sẽ làm cho nó ít di động hơn. Loại bỏ nó.
dimir

2
Trong ví dụ thứ ba của bạn, retval không phải là một biến môi trường. Nó chỉ đơn thuần là một biến shell. Nó sẽ chỉ trở thành một biến môi trường nếu bạn xuất nó. Có lẽ tiêu đề của ví dụ thứ ba nên là "biến toàn cục" thay vì "biến môi trường".
William Pursell

4
Trong ví dụ thứ hai, chứ không phải là gán từ $ ?, nó là thành ngữ hơn để ghi "nếu testlock, sau đó ..."
William Pursell

@WilliamPursell Tôi đã xóa từ 'môi trường' sai. Hãy giữ "$?" cho mục đích sư phạm. Tôi đã kích hoạt cộng đồng Wiki, vì vậy bạn đều miễn phí để cải thiện câu trả lời ;-)
olibre

1
@ManuelJordan, Các hàm chỉ có thể trả về mã thoát và> & 2 nhật ký cho stderror, do đó, tiếng vọng cuối cùng được ghi vào stdout, do đó, hàm gọi CHỈ bắt stdout chứ không phải stderr. Giả sử việc thực thi là một luồng, một tùy chọn tốt hơn là duy trì một biến tùy chỉnh cụ thể như TEST_LOCK_STATUS = "" phương thức bên ngoài mà bất kỳ ai cũng có thể sử dụng sau khi gọi testlock và đặt lại nó mỗi lần khi bắt đầu phương thức
kisna

16

Bạn đang làm việc quá chăm chỉ. Toàn bộ tập lệnh của bạn phải là:

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

nhưng ngay cả điều đó có lẽ là quá dài dòng. Tôi sẽ viết mã nó:

mkdir "$lockdir" || exit 1

nhưng thông báo lỗi kết quả là một chút tối nghĩa.


1
Thông báo lỗi bị thiếu đủ dễ dàng để sửa, mặc dù nó hơi dài dòng hơn: mkdir "$lockdir" || { echo "could not create lock dir" >&2 ; exit 1 ; }(lưu ý ;trước dấu ngoặc nhọn đóng). Ngoài ra, tôi thường xác định một hàm fail nhận một tham số thông báo tùy chọn mà nó in ra stderr và sau đó thoát với mã trả về 1, cho phép tôi sử dụng hàm dễ đọc hơn mkdir "$lockdir" || fail "could not create lock dir".
blubberdiblub

@blubberdiblub: nhưng hàm fail không thể thoát khỏi hàm hoặc script "hiện tại" được không? vì vậy bạn phải sử dụng cmd || fail "error msg" || return 1nếu bạn muốn làm điều đó, phải không?
Tối đa

@Max không phải là chức năng hiện tại, điều đó đúng. Nhưng nó sẽ thoát khỏi tập lệnh hiện tại, miễn là bạn đã gọi nó dưới dạng lệnh và không tìm nguồn . Tôi thường nghĩ về một failchức năng như vậy chỉ được sử dụng cho các tình huống nguy hiểm.
blubberdiblub

12

Nếu đó chỉ là một thử nghiệm đúng / sai, hãy có chức năng của bạn return 0để thành công và return 1thất bại. Bài kiểm tra sau đó sẽ là:

if function_name; then
  do something
else
  error condition
fi

Chính xác những gì tôi đang tìm kiếm.
Samuel

Có cách nào để sử dụng ký hiệu này cũng như cho các hàm được tham số hóa không?
Alex

@alex bạn có thể cho một ví dụ về ý bạn của "hàm được tham số hóa" không?
glenn Jackman

'myCopyFunc $ {SOURCE} $ {DEST}', trả về 0 nếu thành công. Ví dụ như trong vấn đề này: stackoverflow.com/questions/6212219/…
Alex,

Vâng, đó là hoàn toàn tốt đẹp
glenn Jackman

2

Tôi nghĩ rằng trả về 0 cho succ / 1 cho fail (glenn jackman) và câu trả lời rõ ràng và giải thích của olibre đã nói lên tất cả; chỉ đề cập đến một loại phương pháp tiếp cận "kết hợp" cho các trường hợp kết quả không phải là hệ nhị phân và bạn muốn đặt một biến hơn là "lặp lại" một kết quả (ví dụ: nếu hàm của bạn CŨNG giả sử lặp lại điều gì đó, thì cách tiếp cận này sẽ không làm việc). Sau đó là gì? (bên dưới là Bourne Shell)

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

như trong (vâng, ví dụ hơi ngớ ngẩn, nó chỉ là một .. ví dụ)

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

Trong trường hợp bạn có một số tham số để chuyển cho một hàm và muốn đổi lại một giá trị. Ở đây tôi đang chuyển "12345" làm đối số cho một hàm và sau khi xử lý, biến trở lại XYZ sẽ được gán cho VALUE

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


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

Đầu ra:

something12345
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.