我想从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

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

其他回答

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

我想,所有的选择都已经列举出来了。选择一种可以归结为最适合您的特定应用程序的样式,因此,我想提供一种我认为有用的特定样式。在bash中,变量和函数不在同一个命名空间中。因此,将同名变量视为函数值是一种约定,如果严格应用它,我发现这种约定可以最大限度地减少名称冲突并增强可读性。一个来自现实生活的例子:

UnGetChar=
function GetChar() {
    # assume failure
    GetChar=
    # if someone previously "ungot" a char
    if ! [ -z "$UnGetChar" ]; then
        GetChar="$UnGetChar"
        UnGetChar=
        return 0               # success
    # else, if not at EOF
    elif IFS= read -N1 GetChar ; then
        return 0           # success
    else
        return 1           # EOF
    fi
}

function UnGetChar(){
    UnGetChar="$1"
}

下面是一个使用这些函数的例子:

function GetToken() {
    # assume failure
    GetToken=
    # if at end of file
    if ! GetChar; then
        return 1              # EOF
    # if start of comment
    elif [[ "$GetChar" == "#" ]]; then
        while [[ "$GetChar" != $'\n' ]]; do
            GetToken+="$GetChar"
            GetChar
        done
        UnGetChar "$GetChar"
    # if start of quoted string
    elif [ "$GetChar" == '"' ]; then
# ... et cetera

如您所见,返回状态是供您在需要时使用的,如果不需要则忽略它。“返回”变量同样可以被使用或忽略,但当然只有在函数被调用之后。

当然,这只是一种惯例。您可以在返回之前不设置相关值(因此我的约定总是在函数开始时将其为空),或者通过再次调用函数(可能是间接地)来破坏它的值。不过,如果我发现自己大量使用bash函数,我觉得这种约定非常有用。

相反的情绪,这是一个迹象,一个应该。“转到perl”,我的哲学是,对于管理任何语言的复杂性,约定总是很重要的。

agt@agtsoft:~/temp$ cat ./fc 
#!/bin/sh

fcall='function fcall { local res p=$1; shift; fname $*; eval "$p=$res"; }; fcall'

function f1 {
    res=$[($1+$2)*2];
}

function f2 {
    local a;
    eval ${fcall//fname/f1} a 2 3;
    echo f2:$a;
}

a=3;
f2;
echo after:a=$a, res=$res

agt@agtsoft:~/temp$ ./fc
f2:10
after:a=3, res=

像上面的bstpierre一样,我使用并推荐使用显式命名输出变量:

function some_func() # OUTVAR ARG1
{
   local _outvar=$1
   local _result # Use some naming convention to avoid OUTVARs to clash
   ... some processing ....
   eval $_outvar=\$_result # Instead of just =$_result
}

注意引用$的用法。这将避免将$result中的内容解释为shell特殊字符。我发现这比result=$(some_func "arg1")捕获回声的习惯用法快一个数量级。在MSYS上使用bash时,速度差异似乎更加显著,从函数调用中捕获标准输出几乎是灾难性的。

可以发送一个局部变量,因为局部变量在bash中是动态作用域的:

function another_func() # ARG
{
   local result
   some_func result "$1"
   echo result is $result
}

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)