5

Как вернуть строковое значение из функции Bash

13

Я хотел бы вернуть строку из функции Bash.

Я приведу пример на Java, чтобы продемонстрировать, что именно я хочу сделать:

public String getSomeString() {
  return "tadaa";
}

String variable = getSomeString();

Пример ниже работает в Bash, но существует ли более элегантный способ сделать это?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

Спасибо за любую помощь!

5 ответ(ов)

3

Наилучшего способа, который я знаю, нет. Bash знает только статусные коды (целые числа) и строки, выводимые в stdout.

2

Вы можете создать функцию, которая принимает переменную в качестве первого аргумента и изменяет ее, чтобы вернуть нужную строку.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

Это выведет "foo bar rab oof".

Правка: добавлены кавычки в нужном месте, чтобы обеспечить корректную передачу строк с пробелами, что было указано в комментарии от @Luca Borrione.

Правка: в качестве демонстрации, посмотрите следующую программу. Это универсальное решение: оно даже позволяет вам получить строку в локальной переменной.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "$1='foo bar rab oof'"
}

return_var=''
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar=''
     pass_back_a_string lvar
     echo "lvar='$lvar' локально"
}

call_a_string_func
echo "lvar='$lvar' глобально"

Вывод будет следующим:

+ return_var=
+ pass_back_a_string return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local lvar=
+ pass_back_a_string lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' локально'
lvar='foo bar rab oof' локально
+ echo 'lvar='\'''\'' глобально'
lvar='' глобально

Правка: демонстрация того, что значение оригинальной переменной доступно в функции, что было ошибочно подвергнуто критике комментатором @Xichen Li.

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "echo in pass_back_a_string, original $1 is \$$1"
    eval "$1='foo bar rab oof'"
}

return_var='original return_var'
pass_back_a_string return_var
echo $return_var

function call_a_string_func() {
     local lvar='original lvar'
     pass_back_a_string lvar
     echo "lvar='$lvar' локально"
}

call_a_string_func
echo "lvar='$lvar' глобально"

Результат будет таким:

+ return_var='original return_var'
+ pass_back_a_string return_var
+ eval 'echo in pass_back_a_string, original return_var is $return_var'
++ echo in pass_back_a_string, original return_var is original return_var
in pass_back_a_string, original return_var is original return_var
+ eval 'return_var='\''foo bar rab oof'\'''
++ return_var='foo bar rab oof'
+ echo foo bar rab oof
foo bar rab oof
+ call_a_string_func
+ local 'lvar=original lvar'
+ pass_back_a_string lvar
+ eval 'echo in pass_back_a_string, original lvar is $lvar'
++ echo in pass_back_a_string, original lvar is original lvar
in pass_back_a_string, original lvar is original lvar
+ eval 'lvar='\''foo bar rab oof'\'''
++ lvar='foo bar rab oof'
+ echo 'lvar='\''foo bar rab oof'\'' локально'
lvar='foo bar rab oof' локально
+ echo 'lvar='\'''\'' глобально'
lvar='' глобально
1

Ваш вопрос затрагивает важные моменты относительно области видимости переменных в Bash. Как указано в руководстве пользователя, все переменные, объявленные внутри функции, будут общими с вызывающей средой, если они не объявлены как локальные с помощью ключевого слова local.

В приведенном вами примере кода:

#!/bin/bash

f()
{
    echo function starts
    local WillNotExists="It still does!"
    DoesNotExists="It still does!"
    echo function ends
}

echo $DoesNotExists #Должно напечатать пустую строку
echo $WillNotExists #Должно напечатать пустую строку
f                   #Вызов функции
echo $DoesNotExists #Должно напечатать "It still does!"
echo $WillNotExists #Должно напечатать пустую строку

Результат выполнения скрипта действительно подтверждает это. При вызове функции f:

  • Переменная WillNotExists, объявленная как local, не доступна вне функции, поэтому при ее выводе после вызова функции будет возвращено пустое значение.
  • Переменная DoesNotExists была объявлена без использования local, что делает ее доступной в глобальной области видимости. Поэтому, после вызова функции, вывод echo $DoesNotExists покажет "It still does!".

Таким образом, ваш скрипт демонстрирует поведение, соответствующее заявлению в man-странице Bash. Этот же принцип также справедлив для pdksh и ksh, как вы отметили.

0

С версии 4.3 Bash, выпущенной в феврале 2014 года, появилась явная поддержка ссылочных переменных или "именных ссылок" (namerefs). Это улучшение позволяет избежать использования eval и обеспечивает более понятный и безопасный способ работы со ссылками на переменные в ваших скриптах.

Использование команды declare или typeset с опцией -n позволяет создать именную ссылку на другую переменную. Все обращения и присваивания к этой ссылке будут производиться на переменной, имя которой указано в значении ссылки. Пример команды:

declare [-aAfFgilnrtux] [-p] [name[=value] ...]

Где ключ -n предоставляет переменной атрибут ссылки. Вместе с тем, именные ссылки нельзя применять к массивам.

Когда declare или typeset используются внутри функции, они создают локальные переменные по умолчанию, если не указана опция -g. Пример использования именной ссылки в функции:

function return_a_string () {
    declare -n ret=$1
    local MYLIB_return_a_string_message="The date is "
    MYLIB_return_a_string_message+=$(date)
    ret=$MYLIB_return_a_string_message
}

При этом, вызвав функцию как показано ниже, можно получить ожидаемый результат:

$ return_a_string result; echo $result
The date is 20160817

Обратите внимание, что когда вы используете встроенную команду declare внутри функции, переменная по умолчанию будет локальной. Опцию -n также можно использовать с local, что позволяет лучше структурировать ваш код и избежать конфликтов с именами переменных.

Я предпочитаю различать "важные" переменные declare от "обычных" локальных переменных, что делает код более читаемым и структурированным.

Также, как показали тесты, функция работает корректно при вызове с различными переменными, например:

$ return_a_string ret; echo $ret
The date is 20170104

В общем, именные ссылки в Bash — это мощный инструмент для работы с переменными, позволяющий управлять ими более гибко и понятно.

0

Вы также можете захватить вывод функции:

#!/bin/bash
function getSomeString() {
     echo "тандаа!"
}

return_var=$(getSomeString)
echo $return_var
# Альтернативный синтаксис:
return_var=`getSomeString`
echo $return_var

Это может выглядеть немного странно, но, на мой взгляд, это лучше, чем использование глобальных переменных. Передача параметров работает как обычно, просто поместите их внутри фигурных скобок или обратных кавычек.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь