需要通过bash中的函数引用来捕获stdout并修改变量

krb*_*686 5 bash

我可以轻松地从函数调用(在子shell中)捕获标准输出到变量:

val="$(get_value)"
Run Code Online (Sandbox Code Playgroud)

我还可以通过引用修改 shell 中变量(例如,数组),可以在同一个 shell 中使用以下内容进行修改:

function array.delete_by_index {
    local array_name="$1"
    local key="$2"

    unset "$array_name[$key]"
    eval "$array_name=(\"\${$array_name[@]}\")"
}

array.delete_by_index "array1" 0
Run Code Online (Sandbox Code Playgroud)

但是我正在努力弄清楚如何做,以干净的方式同时进行。我想要的一个例子是从数组中弹出一个值:

function array.pop {
    local array_name="$1"

    local last_index=$(( $(eval "echo \${#$array_name[@]}") - 1 ))
    local tmp="$array_name[\"$last_index\"]"
    echo "${!tmp}"

    # Changes "$array_name" here, but not in caller since this is a sub shell
    array.delete_by_index "$array_name" $last_index
}

val="$(array.pop "array1")"
Run Code Online (Sandbox Code Playgroud)

在我看来,将标准输出捕获到变量的所有形式都需要 bash 中的子 shell,而使用子 shell 将不允许我在调用者的上下文中通过引用更改值。

我想知道是否有人知道一种神奇的 bashism 来实现这一目标?我并不特别想要在文件系统上使用任何类型的文件/fifo 的解决方案。

这个问题的第二个答案似乎表明这在kshusing 中是可能的val="${ cmd; }",因为这个构造显然允许捕获输出,但不使用子外壳。所以是的,我可以在技术上切换到 ksh,但我想知道这在 bash 中是否可行。

Kus*_*nda 6

这适用于bash(自 4.3 版以来)和ksh93. 为了“bashify”它,替换所有typesetlocal在功能,以及typeset在全球范围内declare(同时保持所有选项!)。老实说,我不知道为什么 Bash 对只是typeset.

function stack_push
{
    typeset -n _stack="$1"
    typeset element="$2"

    _stack+=("$element")
}

function stack_pop
{
    typeset -n _stack="$1"
    typeset -n _retvar="$2"

    _retvar="${_stack[-1]}"

    unset _stack[-1]
}

typeset -a stack=()

stack_push stack "hello"
stack_push stack "world"

stack_pop stack value
printf '%s ' "$value"

stack_pop stack value
printf '%s\n' "$value"
Run Code Online (Sandbox Code Playgroud)

在函数中使用 nameref 可以避免eval(我从来没有eval在任何脚本中的任何地方使用过!)。通过为stack_pop函数提供存储弹出值的位置,您可以避免使用子shell。通过避开子shell,stack_pop函数可以修改stack外部作用域中变量的值。

函数中局部变量中的下划线是为了避免 nameref 与它引用的变量同名(Bash 不喜欢它,ksh不介意,请参阅此问题)。

ksh你可以写的stack_pop类似功能

function stack_pop
{
    typeset -n _stack="$1"

    printf '%s' "${_stack[-1]}"

    unset _stack[-1]
}
Run Code Online (Sandbox Code Playgroud)

然后用

printf '%s %s\n' "${ stack_pop stack }" "${ stack_pop stack }"
Run Code Online (Sandbox Code Playgroud)

${ ... }$( ... )但不创建子shell相同)

但我不是这个的忠实粉丝。恕我直言,stack_pop不应该将数据发送到标准输出,我不应该调用它${ ... }来获取数据。如果需要,我可能对我的原始文件更满意stack_pop,然后添加一个stack_pop_print执行上述操作的文件。

对于 Bash,你可以stack_pop在我的文章开头使用 ,然后有一个stack_top_print只是将堆栈的顶部元素打印到标准输出,而不删除它(它不能,因为它很可能在$( ... )子shell中运行)。