【发布时间】:2022-01-24 19:46:14
【问题描述】:
我很好奇bash 是否有环境变量堆栈。
作为一个用例的例子,它显然是good practice to unset CDPATH in a script。当然,你想在之后恢复 CDPATH 的原始值,这样脚本就不会破坏用户的环境。
如果我们能做到就好了
#!/usr/bin/env bash
pushenv CDPATH
<script here>
popenv CDPATH
然后就这样吧。不幸的是,人们不得不摆弄这些恶作剧:
#!/usr/bin/env bash
old_CDPATH="$CDPATH"
unset CDPATH
<script here>
CDPATH="$old_CDPATH"
unset old_CDPATH
除了凌乱之外,这也是一个真正的问题。如果变量old_CDPATH 可能已经在使用;如果在<script here> 本身中使用$CDPATH,它将中断。
这就是堆栈的用途,也是存在pushd 和popd bash 内置函数的原因。但是对于任意变量,我们有类似的内置功能吗?
你可能会说“只在子 shell 中做事,不要导出 old_CDPATH”,我会说,不,有时你必须在同一个 shell 中运行代码(例如源脚本)(例如,如果这些脚本的目的是修改环境变量。
那么,我们如何获得类似堆栈的行为?我们可以用这个成语:
#!/usr/bin/env bash
declare -a arr_old_CDPATH
function do_thing() {
arr_old_CDPATH+="$CDPATH"
unset CDPATH
<script here>
CDPATH="${arr_old_CDPATH[-1]}"
unset arr_old_CDPATH[-1]
}
do_thing
我们甚至可以将其打包成pop 和push 函数:
#!/usr/bin/env bash
function push() { # $1 = stack (array) name, $2 = var name
local -n arr=$1
arr+=("${!2}")
unset "$2"
}
function pop() { # $1 = stack (array) name
local -n arr=$1
echo "${arr[-1]}"
unset arr[-1]
}
push cdstack CDPATH
<script here>
CDPATH="$(pop CDPATH)"
我们还可以变得更复杂,为每个变量自动创建一个堆栈。然后我们就可以“pop CDPATH”了:
declare _env_stack_name
env_stack_set_name() { # $1 = var name
local stack_name=_stack_env_$1
[ -v $stack_name ] || declare -ag $stack_name
_env_stack_name=$stack_name
}
pushenv() { # $1 = var name
env_stack_set_name $1
local -n arr=$_env_stack_name
arr+=("${!1}")
unset "$1"
}
popenv() { # $1 = var name
env_stack_set_name $1
local -n arr=$_env_stack_name
local -n var=$1
var=${arr[-1]}
unset arr[-1]
}
pushenv CDPATH
<script here>
popenv CDPATH
对于需要在单个 shell 中运行的复杂脚本所必需的基本堆栈功能而言,这一切似乎都太过分了。
有没有比 pushd/popd 更好的习惯用法或更通用的内置 bash 可以允许推送/弹出环境变量?
【问题讨论】:
-
不,没有用于环境变量的内置堆栈。
标签: bash