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


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.