我查了一下,找不到答案,如果之前有人问过,我提前道歉。
我在 FreeBSD 上使用 shell (/bin/sh
,并且我想将所有 shell 转储到标准输出 (不是环境)以_myvar_
.我能得到的最接近的是set | grep '^_myvar_'
,但只转储多行变量的第一行(有些是多行的,我需要完整的),并且在病理边缘情况下可能容易出错,例如包含“的字符串迈瓦尔“这恰好在该部分之前发生换行。
如果我可以只列出变量名称(而不是值),那么我可以在 a 中进行过滤do...read...while
并一次获取具有匹配名称的变量的值,但我找不到一种方法来做到这一点。我也无法过滤完整的输出,因为没有确定的方法来判断输出行是否包含延续或新变量,这对于包含_myvar_
、 =
或newline (\n)
字符或可能尾随的字符串不存在边缘情况问题空间。我不想修改环境,因为代码包含在其他代码中,并且环境必须对其保持稳定。
如果有帮助的话,输出/列表包含任何匹配的环境变量都不是问题(如果存在 - 它们极不可能)
有没有办法做到这一点?
答案1
由于set
FreeBSD 的内置sh
输出格式适合重新输入到 shell,因此您可以执行以下操作:
out() case $1 in (_myvar_*) printf '%s\n' "${1%%=*}"; esac
eval "$(set | sed 's/^/out /')"
即为set
with输出的每一行添加前缀"out "
,并将其计算为 shell 代码(其中out
是一个打印其第一个参数的子字符串到第一个参数的函数=
)。
sed
也会插入"out "
多行变量的内容,但它仍然会包含在我们的out
函数接收并超过第一个的参数中=
,因此在我们不显示的部分中。
例如,对于set
如下输出:
TERM=xterm
USER=stephane
_myvar_foo='line1
line2
line3'
我们将评估:
out TERM=xterm
out USER=stephane
out _myvar_foo='line1
out line2
out line3'
但这仍然没问题,因为out
这 3 个变量只调用了 3 次。
要打印变量名称和值:
out() case $1 in (_myvar_*)
eval 'printf "name: \"%s\" value: \"%s\"\n" "${1%%=*}" "${'"${1%%=*}"'}"'
esac
eval "$(set | sed 's/^/out /')"
注意它只输出变量,而不是其他类型的参数,例如$-
,位置参数......
该方法仅适用于仅输出的sh
实现set
标量变量(不适用于数组或关联数组或复合变量,这out var=(x)
会成为语法错误)。那些具有其他变量类型的 shell 通常也具有更好的自省功能。
在zsh
:
typeset -pm '_myvar_*'
或仅用于名称
echo $parameters[(I)_myvar_*]
在bash
:
v=("${!_myvar_@}"); ((${#v[@]})) && typeset -p -- "${v[@]}"
echo "${!_myvar_@}"
答案2
我最终得到了 @StéphaneChazelas 解决方案的以下简化,因为我不需要更多。将其发布在下面以防对任何人有帮助。但 Stéphane 找到了答案,这只是它的一个修改版本,仅此而已。
#!/bin/sh
get_vars() {
if printf '%s' "$1" | grep -qE '^_myvar_'; then
printf '%s\n' "$1" | sed 's/^get_vars //'
fi
}
eval "$( set | sed 's/^/get_vars /' )"
该eval
调用为每行添加前缀,但由于输出其值的方式,它实际上为每个变量set
创建一个命令。get_vars VARNAME=VALUE
评估它会执行命令。然后每次调用时,get_vars
检查当前行中捕获的变量是否与所需的正则表达式匹配,如果是,则从"get_vars "
第二行和以后的行中删除前缀,并显示结果。