访问bash中变量的函数定义时间(而不是计算时间)值

访问bash中变量的函数定义时间(而不是计算时间)值,bash,shell,Bash,Shell,我希望我能做这样的事情,结果会是“你好” 这个问题意味着我想在定义时捕获一些全局值的值,通常通过source blabla.bash使用,并希望它定义一个捕获当前变量值的函数。 函数在运行时进行计算,而不是在定义时进行计算。由于您希望捕获定义时存在的变量,因此需要在定义时分配一个单独的变量 foo="hello" # By convention, global variables prefixed by a function name and double underscore are fo

我希望我能做这样的事情,结果会是“你好”


这个问题意味着我想在定义时捕获一些全局值的值,通常通过
source blabla.bash
使用,并希望它定义一个捕获当前变量值的函数。

函数在运行时进行计算,而不是在定义时进行计算。由于您希望捕获定义时存在的变量,因此需要在定义时分配一个单独的变量

foo="hello"

# By convention, global variables prefixed by a function name and double underscore are for
# the exclusive use of that function.
readonly dummy__foo="$foo" # capture foo as of dummy definition time, and prevent changes
dummy() {
  local local_foo=$dummy__foo # ...and refer to that captured copy
  echo "$local_foo"
}

foo=""
dummy

疯狂的方式 但是,如果您愿意犯下危害人类罪,则可以通过生成代码来获取价值。例如:

# usage: with_locals functionname k1=v1 [k2=v2 [...]]
with_locals() {
  local func_name func_text assignments

  func_name=$1; shift || return ## fail if out of arguments
  (( $# )) || return            ## noop if not given at least one assignment

  func_text=$(declare -f "$func_name")

  for arg; do
    if [[ $arg = *=* ]]; then   ## if we already look like an assignment, leave be
      printf -v arg_q 'local %q; ' "$arg"
    else                        ## otherwise, assume we're a bare name and run a lookup
      printf -v arg_q 'local %q=%q; ' "$arg" "${!arg}"
    fi
    assignments+="$arg_q"
  done
  # suffix first instance of { in the function definition with our assignments
  eval "${func_text/{/{ $assignments}"
}
……此后:

foo=hello

dummy() {
  local local_foo="$foo"
  echo "$local_foo"
}
with_locals dummy foo ## redefine dummy to always use the current value of "foo"

foo=''
dummy

好的,您可以注释掉或删除
foo='
行,这样就可以了。函数
dummy
在您调用它之前不会执行,这是在您清除了
foo
值之后执行的,因此您可以得到一个空行。希望这有帮助。

除非bash调用函数,否则无法在函数内执行代码。只有另一种方法可以调用其他函数,用于定义要调用的函数。
这就是动态函数定义

我不相信你想要那样

另一种方法是存储foo的值(调用函数),然后在值更改后再次调用它。像这样的东西:

#!/bin/bash

foo="hello"

dummy() {
    ${global_foo+false} && 
        global_foo="$foo" || 
            echo "old_foo=$global_foo new_foo=$foo" 
}
dummy

foo='new'
dummy

foo="a whole new foo"
dummy
调用它将打印:

$ ./script
old_foo=hello new_foo=new
old_foo=hello new_foo=a whole new foo

由于我不确定这是否能解决您真正的问题,我只想:希望这能有所帮助。

受@CharlesDuffy的启发,我认为使用eval可能会解决一些问题,示例可以修改如下:

#!/bin/bash

foo="hello"

eval "
dummy() {
  local local_foo=$foo
  echo \$local_foo
}
"

foo=''

dummy
这将给出结果“hello”而不是“nothing”


@CharlesDuffy指出,这种解决方案相当危险:

local\u foo=$foo存在危险的错误:如果您的foo值包含 一个扩展,例如$(rm-rf$HOME),它将被执行


使用eval在性能上是好的,但是在安全性上是坏的。因此,我建议@CharlesDuffy给出答案。

因为在调用伪函数之前,您有意这样做
foo='
,所以您几乎不能这样做。此外,通常您会使用
local\u foo=“$foo”
而不是
echo
来调用和捕获结果。如果您是bash新手,我建议您……那么您希望在函数定义时捕获变量的值,而不是在求值时?考虑编辑你的问题,具体地说,这就是你想要的(而且,不,你不能做它而不创建一个单独的变量)…嗯,创建一个单独的变量,或者做一些非常糟糕的事情(动态地生成你的函数)。BTW,你的真正目标是什么?比如说,希望您不打算通过从局部变量集中清除密码来向用户隐藏密码——考虑到访问函数的原始文本是多么微不足道,这样的行为实际上是没有用的。很难相信这告诉OP他们不知道的任何事情——如果不是为了说明他们打算使用定义
dummy
时存在的值,他们为什么要把
foo=''
放在那里,vs运行
dummy
时存在的值?使用
local\ucode>前缀命名全局变量有点误导,不是吗?(如果OP想要将内容复制到另一个全局变量,我认为有一些方法比第一次调用函数的行为与在同一个shell或其子shell中的未来调用的行为完全不同更令人惊讶。)@charlesduff当然,让我来说明一下全局变量。是的,这就是为什么我从一开始就称这个解决方案为“hackish”。您可以修改它以使其更安全:如果您运行
printf-v foo_q“%q'$foo”
,然后在
eval
中使用
$foo_q
,那么它就不会像前面所描述的那样被利用。在生成要
eval
的文本时,也可以使用
$(printf'%q'$foo')
,但这会导致性能下降,因为它会创建一个子shell,除非您的解释器(如ksh93)足够聪明,能够对其进行优化。(当然,我自己的答案也会支付一次子shell惩罚,以捕获
declare-f
的输出——我想这就是你在谈论手写
eval
时所指的更快;在现代unixlikes上,子shell相对便宜,但肯定有bash运行的平台——比如Cygwin——wh在它们更贵之前)。
#!/bin/bash

foo="hello"

eval "
dummy() {
  local local_foo=$foo
  echo \$local_foo
}
"

foo=''

dummy