列出具有固定前缀的 shell 变量

列出具有固定前缀的 shell 变量

我查了一下,找不到答案,如果之前有人问过,我提前道歉。

我在 FreeBSD 上使用 shell (/bin/sh,并且我想将所有 shell 转储到标准输出 (不是环境)以_myvar_.我能得到的最接近的是set | grep '^_myvar_',但只转储多行变量的第一行(有些是多行的,我需要完整的),并且在病理边缘情况下可能容易出错,例如包含“的字符串迈瓦尔“这恰好在该部分之前发生换行。

如果我可以只列出变量名称(而不是值),那么我可以在 a 中进行过滤do...read...while并一次获取具有匹配名称的变量的值,但我找不到一种方法来做到这一点。我也无法过滤完整的输出,因为没有确定的方法来判断输出行是否包含延续或新变量,这对于包含_myvar_=newline (\n)字符或可能尾随的字符串不存在边缘情况问题空间。我不想修改环境,因为代码包含在其他代码中,并且环境必须对其保持稳定。

如果有帮助的话,输出/列表包含任何匹配的环境变量都不是问题(如果存在 - 它们极不可能)

有没有办法做到这一点?

答案1

由于setFreeBSD 的内置sh输出格式适合重新输入到 shell,因此您可以执行以下操作:

out() case $1 in (_myvar_*) printf '%s\n' "${1%%=*}"; esac
eval "$(set | sed 's/^/out /')"

即为setwith输出的每一行添加前缀"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 "第二行和以后的行中删除前缀,并显示结果。

相关内容