string 如何从 Bash 函数返回字符串值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3236871/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 00:48:27  来源:igfitidea点击:

How to return a string value from a Bash function

stringbashfunctionreturn-value

提问by Tomas F

I'd like to return a string from a Bash function.

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

I'll write the example in java to show what I'd like to do:

我将用 java 编写示例以显示我想做的事情:

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

String variable = getSomeString();

The example below works in bash, but is there a better way to do this?

下面的示例适用于 bash,但有没有更好的方法来做到这一点?

function getSomeString {
   echo "tadaa"
}

VARIABLE=$(getSomeString)

采纳答案by Philipp

There is no better way I know of. Bash knows only status codes (integers) and strings written to the stdout.

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

回答by bstpierre

You could have the function take a variable as the first arg and modify the variable with the string you want to return.

您可以让该函数将一个变量作为第一个 arg 并使用您要返回的字符串修改该变量。

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

return_var=''
pass_back_a_string return_var
echo $return_var

Prints "foo bar rab oof".

打印“foo bar rab oof”。

Edit: added quoting in the appropriate place to allow whitespace in string to address @Luca Borrione's comment.

编辑:在适当的地方添加引号以允许字符串中的空格来解决@Luca Borrione 的评论。

Edit: As a demonstration, see the following program. This is a general-purpose solution: it even allows you to receive a string into a local variable.

编辑:作为演示,请参阅以下程序。这是一个通用解决方案:它甚至允许您将字符串接收到局部变量中。

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "='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' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

This prints:

这打印:

+ 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'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

Edit: demonstrating that the original variable's value isavailable in the function, as was incorrectly criticized by @Xichen Li in a comment.

编辑:这表明原始变量的值在功能可用,进行了错误@Xichen李在评论批评。

#!/bin/bash
set -x
function pass_back_a_string() {
    eval "echo in pass_back_a_string, original  is $"
    eval "='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' locally"
}

call_a_string_func
echo "lvar='$lvar' globally"

This gives output:

这给出了输出:

+ 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'\'' locally'
lvar='foo bar rab oof' locally
+ echo 'lvar='\'''\'' globally'
lvar='' globally

回答by Vicky Ronnen

All answers above ignore what has been stated in the man page of bash.

上面的所有答案都忽略了 bash 手册页中的说明。

  • All variables declared inside a function will be shared with the calling environment.
  • All variables declared local will not be shared.
  • 在函数内声明的所有变量都将与调用环境共享。
  • 所有声明为 local 的变量都不会被共享。

Example code

示例代码

#!/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

And output

和输出

$ 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

Also under pdksh and ksh this script does the same!

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

回答by zenaan

Bash, since version 4.3, feb 2014(?), has explicit support for reference variables or name references (namerefs), beyond "eval", with the same beneficial performance and indirection effect, and which may be clearer in your scripts and also harder to "forget to 'eval' and have to fix this error":

Bash,自 2014 年 2 月 (?) 4.3 版起,显式支持引用变量或名称引用 (namerefs),超越“eval”,具有相同的有益性能和间接效果,并且可能在您的脚本中更清晰,也更难“忘记'eval',必须修复这个错误”:

declare [-aAfFgilnrtux] [-p] [name[=value] ...]
typeset [-aAfFgilnrtux] [-p] [name[=value] ...]
  Declare variables and/or give them attributes
  ...
  -n Give each name the nameref attribute, making it a name reference
     to another variable.  That other variable is defined by the value
     of name.  All references and assignments to name, except for?
     changing the -n attribute itself, are performed on the variable
     referenced by name's value.  The -n attribute cannot be applied to
     array variables.
...
When used in a function, declare and typeset make each name local,
as with the local command, unless the -g option is supplied...

and also:

并且:

PARAMETERS

A variable can be assigned the nameref attribute using the -n option to the declare or local builtin commands (see the descriptions of declare and local below) to create a nameref, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is? referenced or assigned to, the operation is actually performed on the variable specified by the nameref variable's value. A nameref is commonly used within shell functions to refer to a variable whose name is passed as an argument to? the function. For instance, if a variable name is passed to a shell function as its first argument, running

      declare -n ref=

inside the function creates a nameref variable ref whose value is the variable name passed as the first argument. References and assignments to ref are treated as references and assignments to the variable whose name was passed as? $1. If the control variable in a for loop has the nameref attribute, the list of words can be a list of shell variables, and a name reference will be? established for each word in the list, in turn, when the loop is executed. Array variables cannot be given the -n attribute. However, nameref variables can reference array variables and subscripted array variables. Namerefs can be? unset using the -n option to the unset builtin. Otherwise, if unset is executed with the name of a nameref variable as an argument, the variable referenced by? the nameref variable will be unset.

参数

可以使用声明或本地内置命令的 -n 选项为变量分配 nameref 属性(请参阅下面的 declare 和 local 描述)以创建 nameref 或对另一个变量的引用。这允许间接操作变量。每当 nameref 变量是?引用或分配给,该操作实际上是在 nameref 变量的值指定的变量上执行的。nameref 通常在 shell 函数中用于引用名称作为参数传递给的变量?功能。例如,如果变量名称作为第一个参数传递给 shell 函数,则运行

      declare -n ref=

在函数内部创建一个 nameref 变量 ref ,其值是作为第一个参数传递的变量名称。对 ref 的引用和赋值被视为对其名称作为传递的变量的引用和赋值?1 美元。如果 for 循环中的控制变量具有 nameref 属性,则单词列表可以是 shell 变量列表,而名称引用将是?循环执行时,依次为列表中的每个单词建立。数组变量不能被赋予 -n 属性。但是,nameref 变量可以引用数组变量和下标数组变量。namerefs 可以吗?使用 unset 内置命令的 -n 选项取消设置。否则,如果 unset 以 nameref 变量的名称作为参数执行,引用的变量?nameref 变量将被取消设置。

For example (EDIT 2: (thank you Ron) namespaced (prefixed) the function-internal variable name, to minimize external variable clashes, which should finally answer properly, the issue raised in the comments by Karsten):

例如(编辑 2:(谢谢罗恩)命名空间(前缀)函数内部变量名称,以尽量减少外部变量冲突,最终应该正确回答,Karsten 在评论中提出的问题):

#  : string; your variable to contain the return value
function return_a_string () {
    declare -n ret=
    local MYLIB_return_a_string_message="The date is "
    MYLIB_return_a_string_message+=$(date)
    ret=$MYLIB_return_a_string_message
}

and testing this example:

并测试这个例子:

$ return_a_string result; echo $result
The date is 20160817

Note that the bash "declare" builtin, when used in a function, makes the declared variable "local" by default, and "-n" can also be used with "local".

请注意,内置 bash 的“declare”在函数中使用时,会默认使声明的变量为“local”,并且“-n”也可以与“local”一起使用。

I prefer to distinguish "important declare" variables from "boring local" variables, so using "declare" and "local" in this way acts as documentation.

我更喜欢将“重要声明”变量与“无聊的本地”变量区分开来,因此以这种方式使用“声明”和“本地”充当文档。

EDIT 1- (Response to comment below by Karsten) - I cannot add comments below any more, but Karsten's comment got me thinking, so I did the following test which WORKS FINE, AFAICT - Karsten if you read this, please provide an exact set of test steps from the command line, showing the problem you assume exists, because these following steps work just fine:

编辑 1-(对 Karsten 下面评论的回应) - 我不能再在下面添加评论,但 Karsten 的评论让我思考,所以我做了以下测试,效果很好,AFAICT - Karsten 如果你读到这个,请提供一个确切的集合命令行中的测试步骤,显示您假设存在的问题,因为以下步骤工作正常:

$ return_a_string ret; echo $ret
The date is 20170104

(I ran this just now, after pasting the above function into a bash term - as you can see, the result works just fine.)

(我刚刚运行了这个,在将上面的函数粘贴到一个 bash 术语之后 - 正如你所看到的,结果工作得很好。)

回答by Markarian451

Like bstpierreabove, I use and recommend the use of explicitly naming output variables:

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

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

Note the use of quoting the $. This will avoid interpreting content in $resultas shell special characters. I have found that this is an order of magnitude fasterthan the result=$(some_func "arg1")idiom of capturing an echo. The speed difference seems even more notable using bash on MSYS where stdout capturing from function calls is almost catastrophic.

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

It's ok to send in a local variables since locals are dynamically scoped in bash:

可以发送本地变量,因为本地变量在 bash 中是动态范围的:

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

回答by chiborg

You could also capture the function output:

您还可以捕获函数输出:

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

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

Looks weird, but is better than using global variables IMHO. Passing parameters works as usual, just put them inside the braces or backticks.

看起来很奇怪,但比使用全局变量更好恕我直言。传递参数照常工作,只需将它们放在大括号或反引号内。

回答by Tomasz ?uk

The most straightforward and robust solution is to use command substitution, as other people wrote:

最直接和强大的解决方案是使用命令替换,正如其他人所写:

assign()
{
    local x
    x="Test"
    echo "$x"
}

x=$(assign) # This assigns string "Test" to x

The downside is performance as this requires a separate process.

缺点是性能,因为这需要一个单独的过程。

The other technique suggested in this topic, namely passing the name of a variable to assign to as an argument, has side effects, and I wouldn't recommend it in its basic form. The problem is that you will probably need some variables in the function to calculate the return value, and it may happen that the name of the variable intended to store the return value will interfere with one of them:

本主题中建议的另一种技术,即传递要赋值的变量的名称作为参数,有副作用,我不会推荐它的基本形式。问题是您可能需要在函数中使用一些变量来计算返回值,并且可能会发生用于存储返回值的变量名称会干扰其中之一:

assign()
{
    local x
    x="Test"
    eval "=$x"
}

assign y # This assigns string "Test" to y, as expected

assign x # This will NOT assign anything to x in this scope
         # because the name "x" is declared as local inside the function

You might, of course, not declare internal variables of the function as local, but you really should always do it as otherwise you may, on the other hand, accidentally overwrite an unrelated variable from the parent scope if there is one with the same name.

当然,您可能不会将函数的内部变量声明为局部变量,但您确实应该始终这样做,否则另一方面,如果存在同名的变量,您可能会意外覆盖父作用域中的不相关变量.

One possible workaround is an explicit declaration of the passed variable as global:

一种可能的解决方法是将传递的变量显式声明为全局:

assign()
{
    local x
    eval declare -g 
    x="Test"
    eval "=$x"
}

If name "x" is passed as an argument, the second row of the function body will overwrite the previous local declaration. But the names themselves might still interfere, so if you intend to use the value previously stored in the passed variable prior to write the return value there, be aware that you must copy it into another local variable at the very beginning; otherwise the result will be unpredictable! Besides, this will only work in the most recent version of BASH, namely 4.2. More portable code might utilize explicit conditional constructs with the same effect:

如果名称“x”作为参数传递,函数体的第二行将覆盖先前的局部声明。但是名称本身可能仍然会干扰,因此如果您打算在将返回值写入那里之前使用先前存储在传递变量中的值,请注意您必须在开始时将其复制到另一个局部变量中;否则后果不堪设想!此外,这仅适用于最新版本的 BASH,即 4.2。更可移植的代码可能会使用具有相同效果的显式条件结构:

assign()
{
    if [[  != x ]]; then
      local x
    fi
    x="Test"
    eval "=$x"
}

Perhaps the most elegant solution is just to reserve one global name for function return values and use it consistently in every function you write.

也许最优雅的解决方案就是为函数返回值保留一个全局名称,并在您编写的每个函数中始终如一地使用它。

回答by Andy

As previously mentioned, the "correct" way to return a string from a function is with command substitution. In the event that the function also needs to output to console (as @Mani mentions above), create a temporary fd in the beginning of the function and redirect to console. Close the temporary fd before returning your string.

如前所述,从函数返回字符串的“正确”方法是使用命令替换。如果函数还需要输出到控制台(如@Mani 上面提到的),请在函数的开头创建一个临时 fd 并重定向到控制台。在返回字符串之前关闭临时 fd。

#!/bin/bash
# file:  func_return_test.sh
returnString() {
    exec 3>&1 >/dev/tty
    local s=
    s=${s:="some default string"}
    echo "writing directly to console"
    exec 3>&-     
    echo "$s"
}

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

executing script with no params produces...

执行没有参数的脚本会产生...

# ./func_return_test.sh
writing directly to console
my_string:  [some default string]

hope this helps people

希望这可以帮助人们

-Andy

-安迪

回答by Fritz G. Mehner

You could use a global variable:

您可以使用全局变量:

declare globalvar='some string'

string ()
{
  eval  "='some other string'"
} # ----------  end of function string  ----------

string globalvar

echo "'${globalvar}'"

This gives

这给

'some other string'

回答by jmb

To illustrate my comment on Andy's answer, with additional file descriptor manipulation to avoid use of /dev/tty:

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

#!/bin/bash

exec 3>&1

returnString() {
    exec 4>&1 >&3
    local s=
    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]"

Still nasty, though.

不过还是很恶心。