Повернення значення з викликаної функції в сценарії оболонки


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ключове слово для визначення функції bash. Це зробило б його менш портативним. Видалення його.
димір

2
У вашому третьому прикладі вилучення не є змінною середовища. Це просто змінна оболонка. Він стане змінною середовища, лише якщо експортуватимете його. Можливо, заголовок третього прикладу має бути "глобальна змінна" замість "змінна середовище".
Вільям Перселл

4
У другому прикладі, замість того, щоб призначати від $?, Більш ідіоматично писати "якщо тестовий блок; тоді ..."
Вільям Перселл

@WilliamPursell Я видалив неправильне слово "оточення". Давайте збережемо "$?" з педагогічною метою. Я ввімкнув спільноту Wiki, тому ви всі вільні, щоб покращити відповідь ;-)
олібре

1
@ManuelJordan, Функції можуть повертати лише коди виходу та> & 2 журнали до stderror, тому останній відлуння записується до stdout, тому функція виклику ТІЛЬКИ захоплює stdout, а не stderr. Припустимо, що виконання є однопоточним, кращим варіантом є підтримка користувацької змінної, специфічної як TEST_LOCK_STATUS = "" поза методом, яку кожен може використовувати після виклику тестового блоку та скидати її кожного разу на початку методу
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, а потім виходить із кодом повернення 1, що дозволяє мені використовувати більш читабельний mkdir "$lockdir" || fail "could not create lock dir".
blubberdiblub

@blubberdiblub: але функція відмови не може вийти з "поточної" функції чи сценарію, чи не так? тож вам доведеться використовувати, cmd || fail "error msg" || return 1якщо ви хочете це зробити, чи не так?
Макс

@Max не поточна функція, це правильно. Але він вийде з поточного сценарію, якщо ви його назвали командою і не отримали його. Зазвичай я думаю про таку failфункцію, яку використовують лише для фатальних ситуацій.
blubberdiblub

12

Якщо це просто правдивий / хибний тест, покладіть свою функцію return 0на успіх і return 1на невдачу. Тест був би:

if function_name; then
  do something
else
  error condition
fi

Саме те, що я шукав.
Самуїл

Чи є спосіб використовувати це позначення також для параметризованих функцій?
Алекс

@alex чи можете ви навести приклад того, що ви маєте на увазі під «параметризованою функцією»?
glenn jackman

'myCopyFunc $ {SOURCE} $ {DEST}', поверніть 0 на успіх. Наприклад, у цьому номері: stackoverflow.com/questions/6212219/…
Алекс

Так, це прекрасно
glenn jackman

2

Я думаю, що повернення 0 для succ / 1 за невдачу (glenn jackman) і чітка та пояснювальна відповідь olibre говорить про все; просто згадати про своєрідний "комбінований" підхід для випадків, коли результати не є двійковими, і ви вважаєте за краще встановити змінну, а не "повторювати" результат (наприклад, якщо ваша функція ТАКОЖ припускає, що щось повторить, такий підхід буде не працює). Що тоді? (нижче - Борн Шелл)

# 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.