我想从Bash函数返回一个字符串。

我将用java写这个例子来说明我想做什么:

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

String variable = getSomeString();

下面的示例可以在bash中工作,但是是否有更好的方法来做到这一点?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

当前回答

您还可以捕获函数输出:

#!/bin/bash
function getSomeString() {
     echo "tadaa!"
}

return_var=$(getSomeString)
echo $return_var
# Alternative syntax:
return_var=`getSomeString`
echo $return_var

看起来很奇怪,但比使用全局变量更好。传递参数和往常一样,只是把它们放在大括号或反勾号内。

其他回答

They key problem of any 'named output variable' scheme where the caller can pass in the variable name (whether using eval or declare -n) is inadvertent aliasing, i.e. name clashes: From an encapsulation point of view, it's awful to not be able to add or rename a local variable in a function without checking ALL the function's callers first to make sure they're not wanting to pass that same name as the output parameter. (Or in the other direction, I don't want to have to read the source of the function I'm calling just to make sure the output parameter I intend to use is not a local in that function.)

解决这个问题的唯一方法是使用一个单独的专用输出变量,比如REPLY(由ev1m4chine建议),或者使用Ron Burk建议的约定。

但是,也可以让函数在内部使用固定的输出变量,然后在上面添加一些糖来对调用者隐藏这一事实,就像我在下面的示例中对call函数所做的那样。把这看作是概念的证明,但关键是

该函数总是将返回值赋给REPLY,也可以像往常一样返回退出码 从调用者的角度来看,返回值可以分配给任何变量(本地或全局),包括REPLY(参见包装器示例)。函数的退出码是传递的,因此在if或while或类似结构中使用它们可以正常工作。 从语法上讲,函数调用仍然是一条简单的语句。

这样做的原因是调用函数本身没有局部变量,并且除了REPLY之外不使用其他变量,从而避免了任何名称冲突的可能性。当调用方定义的输出变量名被赋值时,我们实际上处于调用方的作用域(技术上讲,在调用函数的相同作用域中),而不是在被调用函数的作用域中。

#!/bin/bash
function call() { # var=func [args ...]
  REPLY=; "${1#*=}" "${@:2}"; eval "${1%%=*}=\$REPLY; return $?"
}

function greet() {
  case "$1" in
    us) REPLY="hello";;
    nz) REPLY="kia ora";;
    *) return 123;;
  esac
}

function wrapper() {
  call REPLY=greet "$@"
}

function main() {
  local a b c d
  call a=greet us
  echo "a='$a' ($?)"
  call b=greet nz
  echo "b='$b' ($?)"
  call c=greet de
  echo "c='$c' ($?)"
  call d=wrapper us
  echo "d='$d' ($?)"
}
main

输出:

a='hello' (0)
b='kia ora' (0)
c='' (123)
d='hello' (0)

以上所有答案都忽略bash手册页中所述内容。

函数中声明的所有变量都将与调用环境共享。 所有在本地声明的变量将不会被共享。

示例代码

#!/bin/bash

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

echo $DoesNotExists #Should print empty line
echo $WillNotExists #Should print empty line
f                   #Call the function
echo $DoesNotExists #Should print It still does!
echo $WillNotExists #Should print empty line

和输出

$ sh -x ./x.sh
+ echo

+ echo

+ f
+ echo function starts 
function starts
+ local 'WillNotExists=It still does!'
+ DoesNotExists='It still does!'
+ echo function ends 
function ends
+ echo It still 'does!' 
It still does!
+ echo

同样在pdksh和ksh下,这个脚本也做同样的事情!

为了说明我对Andy的回答的评论,使用额外的文件描述符操作来避免使用/dev/tty:

#!/bin/bash

exec 3>&1

returnString() {
    exec 4>&1 >&3
    local s=$1
    s=${s:="some default string"}
    echo "writing to stdout"
    echo "writing to stderr" >&2
    exec >&4-
    echo "$s"
}

my_string=$(returnString "$*")
echo "my_string:  [$my_string]"

不过还是很恶心。

#实现一个通用的函数返回堆栈:

STACK=()
push() {
  STACK+=( "${1}" )
}
pop() {
  export $1="${STACK[${#STACK[@]}-1]}"
  unset 'STACK[${#STACK[@]}-1]';
}

#用法:

my_func() {
  push "Hello world!"
  push "Hello world2!"
}
my_func ; pop MESSAGE2 ; pop MESSAGE1
echo ${MESSAGE1} ${MESSAGE2}

没有比这更好的方法了。Bash只知道写入标准输出的状态码(整数)和字符串。