如何在bash中修改函数内的全局变量?

时间:2014-05-09 12:44:36

标签: bash variables global-variables eval

我正在处理这个问题:

GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

我有一个如下脚本:

#!/bin/bash

e=2

function test1() {
  e=4
  echo "hello"
}

test1 
echo "$e"

返回:

hello
4

但是如果我将函数的结果赋给变量,则不会修改全局变量e

#!/bin/bash

e=2

function test1() {
  e=4
  echo "hello"
}

ret=$(test1)

echo "$ret"
echo "$e"

返回:

hello
2

在这种情况下我听说过the use of eval,所以我在test1中这样做了:

eval 'e=4'

但结果相同。

你能解释一下为什么不修改它吗?如何在test1中保存ret函数的回声并修改全局变量?

8 个答案:

答案 0 :(得分:73)

当您使用命令替换(即$(...)构造)时,您正在创建子shell。子shell从其父shell继承变量,但这只能以一种方式工作 - 子shell无法修改其父shell的环境。您的变量e在子shell中设置,但不在父shell中设置。将值从子shell传递到其父级有两种方法。首先,您可以输出一些内容到stdout,然后使用命令替换来捕获它:

myfunc() {
    echo "Hello"
}

var="$(myfunc)"

echo "$var"

给出:

Hello

对于0-255之间的数值,您可以使用return将数字作为退出状态传递:

mysecondfunc() {
    echo "Hello"
    return 4
}

var="$(mysecondfunc)"
num_var=$?

echo "$var - num is $num_var"

给出:

Hello - num is 4

答案 1 :(得分:13)

也许你可以使用一个文件,写入文件里面的函数,然后从文件中读取。我已将e更改为数组。在这个例子中,当读回数组时,空格被用作分隔符。

#!/bin/bash

declare -a e
e[0]="first"
e[1]="secondddd"

function test1 () {
 e[2]="third"
 e[1]="second"
 echo "${e[@]}" > /tmp/tempout
 echo hi
}

ret=$(test1)

echo "$ret"

read -r -a e < /tmp/tempout
echo "${e[@]}"
echo "${e[0]}"
echo "${e[1]}"
echo "${e[2]}"

输出:

hi
first second third
first
second
third

答案 2 :(得分:11)

你在做什么,你正在执行test1

$(test1)

在子shell(子shell)中,子shell无法修改父级中的任何内容。

您可以在bash manual

中找到它

请检查:事情产生子shell here

答案 3 :(得分:4)

当我想自动删除我创建的临时文件时,我遇到了类似的问题。我想出的解决方案不是使用命令替换,而是将应该取最终结果的变量名称传递给函数。 E.g。

#! /bin/bash

remove_later=""
new_tmp_file() {
    file=$(mktemp)
    remove_later="$remove_later $file"
    eval $1=$file
}
remove_tmp_files() {
    rm $remove_later
}
trap remove_tmp_files EXIT

new_tmp_file tmpfile1
new_tmp_file tmpfile2

所以,在你的情况下,这将是:

#!/bin/bash

e=2

function test1() {
  e=4
  eval $1="hello"
}

test1 ret

echo "$ret"
echo "$e"

工作并且对“返回值”没有限制。

答案 4 :(得分:1)

这是因为命令替换是在子shell中执行的,所以当子shell继承变量时,当子shell结束时,它们的更改会丢失。

Reference

  

命令替换,用括号分组的命令和异步命令在子shell环境中调用,该环境与shell环境重复

答案 5 :(得分:1)

此问题的一种解决方案是将值存储在一个临时文件中,并在需要时进行读取/写入,而无需引入复杂的功能并大量修改原始功能。

当我不得不在bats测试用例中多次模拟bash函数时,这种方法对我有很大帮助。

例如,您可能拥有:

# Usage read_value path_to_tmp_file
function read_value {
  cat "${1}"
}

# Usage: set_value path_to_tmp_file the_value
function set_value {
  echo "${2}" > "${1}"
}
#----

# Original code:

function test1() {
  e=4
  set_value "${tmp_file}" "${e}"
  echo "hello"
}


# Create the temp file
# Note that tmp_file is available in test1 as well
tmp_file=$(mktemp)

# Your logic
e=2
# Store the value
set_value "${tmp_file}" "${e}"

# Run test1
test1

# Read the value modified by test1
e=$(read_value "${tmp_file}")
echo "$e"

缺点是您可能需要多个临时文件来存储不同的变量。另外,您可能需要发出sync命令以在一次写入和读取操作之间将内容持久存储在磁盘上。

答案 6 :(得分:1)

假设 local -n 可用,以下脚本让函数 test1 修改全局变量:

#!/bin/bash

e=2

function test1() {
  local -n var=$1
  var=4
  echo "hello"
}

test1 e
echo "$e"

给出以下输出:

hello
4

答案 7 :(得分:-1)

您始终可以使用别名:

alias next='printf "blah_%02d" $count;count=$((count+1))'