在bash中,如何将返回值存储在变量中?

60
我知道一些Linux的基本命令,正在尝试编写一些脚本。我已经编写了一个函数,用于计算一个5位数的最后2位数字的和。该函数应该将这个结果连接在最后2位数字之间并返回它。我的目的是为了在其他函数中使用此值,因此需要返回它。例如:如果我有12345,则我的函数将计算4 + 5并返回495。
#!/bin/bash

set -x
echo "enter: "
        read input

function password_formula
{
        length=${#input}
        last_two=${input:length-2:length}
        first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
        second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
        let sum=$first+$second
        sum_len=${#sum}
        echo $second
        echo $sum

        if [ $sum -gt 9 ]
        then
               sum=${sum:1}
        fi

        value=$second$sum$first
        return $value
}
result=$(password_formula)
echo $result

我正在尝试使用echo命令并查看结果,但我得到的输出如下所示。
-bash-3.2$ ./file2.sh 
+++ password_formula
+++ echo 'enter: '
+++ read input
12385
+++ length=8
+++ last_two=85
++++ echo 85
++++ sed -e 's/\(.\)/\1 /g'
++++ awk '{print $2}'
+++ first=5
++++ echo 85
++++ sed -e 's/\(.\)/\1 /g'
++++ awk '{print $1}'
+++ second=8
+++ let sum=5+8
+++ sum_len=2
+++ echo 5
+++ echo 8
+++ echo 13
+++ '[' 13 -gt 9 ']'
+++ sum=3
+++ value=835
+++ return 835
++ result='enter: 
5
8
13'
++ echo enter: 5 8 13
enter: 5 8 13

我还尝试打印结果:

password_formula
RESULT=$?
echo $RESULT

但是这会给出一些未知的值:

++ RESULT=67
++ echo 67
67

我该如何正确存储值并在屏幕上打印出来(以进行双重检查)?

这里使用反引号似乎不合适。你可以使用 first=${last_two:0:1}second=${last_two:1:1},或者 first=${last_two%?}second=${last_two#?} - William Pursell
1
“return”语句对于习惯高级语言的人来说,名称有些误导性 - 更好的名称可能是“exit_status”。如果您想返回由字符串值或数字值> 255组成的结果,则必须通过“echo”或其他输出到stdout的方式完成。 - MikeW
8个回答

49

最简答案:

函数的返回值只能是 0 到 255 范围内的值。要将这个值存储到变量中,必须像以下示例中所示进行:

#!/bin/bash

function returnfunction {
    # example value between 0-255 to be returned 
    return 23
}

# note that the value has to be stored immediately after the function call :
returnfunction
myreturnvalue=$?

echo "myreturnvalue is "$myreturnvalue

2
这是唯一正确的答案。最受欢迎的答案将函数从return更改为echo,在特定情况下可能有效,但当然不是大多数读者在寻找的期望答案。 - Andreas

35
返回值(又称为退出代码)是一个0到255的范围内的值。它用于指示成功或失败,而不是返回信息。任何超出此范围的值都将被包装。
如果要返回信息(例如您的数字),请使用。
echo "$value"

若要打印不想被捕获的额外信息,请使用

echo "my irrelevant info" >&2 

最后,要捕获它,请使用您所做的内容:
 result=$(password_formula)

换句话说:
echo "enter: "
        read input

password_formula()
{
        length=${#input}
        last_two=${input:length-2:length}
        first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
        second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
        let sum=$first+$second
        sum_len=${#sum}
        echo $second >&2
        echo $sum >&2

        if [ $sum -gt 9 ]
        then
               sum=${sum:1}
        fi

        value=$second$sum$first
        echo $value
}
result=$(password_formula)
echo "The value is $result"

问题在于我需要使用标准输出而不是标准错误。 - Tomáš Zato
由于在此帖子中概述的原因,您不能像 OP 的 495 那样使用 $? 恢复返回值。即使您的值适合退出代码的有限范围,重用它也会违反 posix 约定,并且会让其他工具混淆退出代码作为成功指示器。 - that other guy
抱歉,我没有仔细阅读最初的问题,只看了标题 :) 我没有意识到他问如何传递大于255的值。正确的答案是这是设计上不可能的,但有一些解决方法,比如使用stdout。 - Eric
虽然返回值确实不能存储>255,因此您的答案在解释上满足了提问者的需求,但它未能真正回答所问的问题。远远有更多的人(包括已经来过这里的人)会寻找实际问题的答案,而不是解决提问者想要解决的问题。出于这个原因,我会给这个答案点踩。如果您编辑您的答案以实际回答所问的问题,我会改变我的评价。 - Kurt Fitzner
谢谢!这非常有帮助。 - Felipe

15

这很容易,你只需要回显你需要返回的值,然后像下面一样进行捕获

demofunc(){
    local variable="hellow"
    echo $variable    
}

val=$(demofunc)
echo $val

1
这应该是被接受的答案。当前的回答在语法方面不完整且不正确。 - Buffalo
我同意。这个是最容易使用的。 - Justin Putney
2
如果在echo $variable之前添加echo 555或类似的内容,那么这将与$variable连接在一起,这是不好的。 - Groosha
1
请注意,$(demofunc) 在子shell中执行 demofuncdemofunc 中的 echo 不会在当前shell中显示。因此,我认为更好的答案是 https://dev59.com/M2Up5IYBdhLWcg3w1qIi#35223671。 - Roland Puntaier

14

像这样使用特殊的 Bash 变量 "$?":

function_output=$(my_function)
function_return_value=$?

4
你的意思是应该使用 $(my_function) 而不是 ${my_function},对吗? - Charles Duffy
1
我才意识到返回值并不是控制台输出,而我已经是一名有16年经验的老手了。 - Sridhar Sarnobat

6
上面的答案建议将函数更改为echo数据而不是返回使用,以便可以捕获它。
对于不能修改其返回值需要保存到变量的函数或程序(例如test / [,它返回0/1成功值),在命令替换内echo $?:
# Test if we're remote.
isRemote="$(test -z "$REMOTE_ADDR"; echo $?)"
# Or:
isRemote="$([ -z "$REMOTE_ADDR" ]; echo $?)"

# Additionally you may want to reverse the 0 (success) / 1 (error) values
# for your own sanity, using arithmetic expansion:
remoteAddrIsEmpty="$([ -z "$REMOTE_ADDR" ]; echo $((1-$?)))"

例如。
$ echo $REMOTE_ADDR

$ test -z "$REMOTE_ADDR"; echo $?
0
$ REMOTE_ADDR=127.0.0.1
$ test -z "$REMOTE_ADDR"; echo $?
1
$ retval="$(test -z "$REMOTE_ADDR"; echo $?)"; echo $retval
1
$ unset REMOTE_ADDR
$ retval="$(test -z "$REMOTE_ADDR"; echo $?)"; echo $retval
0

对于一个既打印数据又有返回值需要保存的程序,需要将返回值从输出中单独捕获:

# Two different files, 1 and 2.
$ cat 1
1
$ cat 2
2
$ diffs="$(cmp 1 2)"
$ haveDiffs=$?
$ echo "Have differences? [$haveDiffs] Diffs: [$diffs]"
Have differences? [1] Diffs: [1 2 differ: char 1, line 1]
$ diffs="$(cmp 1 1)"
$ haveDiffs=$?
$ echo "Have differences? [$haveDiffs] Diffs: [$diffs]"
Have differences? [0] Diffs: []

# Or again, if you just want a success variable, reverse with arithmetic expansion:
$ cmp -s 1 2; filesAreIdentical=$((1-$?))
$ echo $filesAreIdentical
0

1
由于echo语句导致的。您可以将您的echo切换为print,并使用echo返回。以下是有效的。
#!/bin/bash

set -x
echo "enter: "
read input

function password_formula
{
        length=${#input}
        last_two=${input:length-2:length}
        first=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $2}'`
        second=`echo $last_two| sed -e 's/\(.\)/\1 /g'|awk '{print $1}'`
        let sum=$first+$second
        sum_len=${#sum}
        print $second
        print $sum

        if [ $sum -gt 9 ]
        then
           sum=${sum:1}
        fi

        value=$second$sum$first
        echo $value
}
result=$(password_formula)
echo $result

非常感谢你们提供的详细解释,现在它可以正常工作了。 - itsh
问题是如何获取“返回值”,而不是echo的值。 - Tomáš Zato
这是如何返回一个值,而不是状态码。使用 return 返回一个状态码。试图将其过载为值是错误的,因为状态码值受到限制。 - Daniel Holmes

0

好的,如果我们设置了errexit,那么这些主要答案就有问题。

#!/bin/bash
set -o errexit

my_fun() {
    # returns 0 if the first arguments is "a"
    # 1 otherwise
    [ "${1}" = "a" ]
}

my_fun "a"
echo "a=$?"
my_fun "b"
echo "b=$?"

在这种情况下,当结果不为0时,bash就会退出,例如,这只会打印一行。
./test_output.sh 
a=0

正如已经很好地在这里所说,最正确的答案可能是这样的:

# like this
my_val=0 ; my_fun "a" || my_val=$?
echo "a=${my_val}"

# or this
my_fun "b" && my_val=0 || my_val=$?
echo "b=${my_val}"

这将所有值正确地打印,没有错误

a=0
b=1

我不确定"echo"的实现是否正确,因为我仍然不清楚$()是否创建了一个子shell。 例如,我有一个函数,在函数中打开文件描述符并返回该数字,似乎bash在退出函数后关闭了fd。 (如果有人能帮我解决这个问题:-))


0

可以使用类似这样的方式,仍然保持return返回控制信号)和echo返回信息)以及日志语句(打印调试/信息消息)的含义。

v_verbose=1
v_verbose_f=""         # verbose file name
FLAG_BGPID=""

e_verbose() {
        if [[ $v_verbose -ge 0 ]]; then
                v_verbose_f=$(tempfile)
                tail -f $v_verbose_f &
                FLAG_BGPID="$!"
        fi
}

d_verbose() {
        if [[ x"$FLAG_BGPID" != "x" ]]; then
                kill $FLAG_BGPID > /dev/null
                FLAG_BGPID=""
                rm -f $v_verbose_f > /dev/null
        fi
}

init() {
        e_verbose

        trap cleanup SIGINT SIGQUIT SIGKILL SIGSTOP SIGTERM SIGHUP SIGTSTP
}

cleanup() {
        d_verbose
}

init

fun1() {
    echo "got $1" >> $v_verbose_f
    echo "got $2" >> $v_verbose_f
    echo "$(( $1 + $2 ))"
    return 0
}

a=$(fun1 10 20)
if [[ $? -eq 0 ]]; then
    echo ">>sum: $a"
else
    echo "error: $?"
fi
cleanup

在这里,我将调试消息重定向到单独的文件中,该文件由tail监视,如果有任何更改,则打印更改,trap用于确保后台进程始终结束。

也可以通过重定向到/dev/stderr来实现此行为,但是在将一个命令的输出管道传输到另一个命令的输入时,可以看到差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接