我可以轻松地从函数调用(在子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 的解决方案。
这个问题的第二个答案似乎表明这在ksh
using 中是可能的val="${ cmd; }"
,因为这个构造显然允许捕获输出,但不使用子外壳。所以是的,我可以在技术上切换到 ksh,但我想知道这在 bash 中是否可行。
这适用于bash
(自 4.3 版以来)和ksh93
. 为了“bashify”它,替换所有typeset
与local
在功能,以及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中运行)。