如何列出在 bash 脚本中声明的变量?

在 bash 中的脚本中,有许多变量,我必须创建一些东西来将它们保存到文件中。 我的问题是如何列出我的脚本中声明的所有变量,并获得如下列表:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi
101138 次浏览

set将输出变量,不幸的是,它也将输出定义的函数。

幸运的是,POSIX 模式只输出变量:

( set -o posix ; set ) | less

通过管道连接到 less,或者重定向到需要选项的位置。

所以要在脚本中声明变量:

( set -o posix ; set ) >/tmp/variables.before
source script
( set -o posix ; set ) >/tmp/variables.after
diff /tmp/variables.before /tmp/variables.after
rm /tmp/variables.before /tmp/variables.after

(或者至少是以此为基础的东西: ——)

如果您可以后处理(如前所述) ,您可以在脚本的开头和结尾(每个脚本都对应一个不同的文件)放置一个 set调用,并对这两个文件进行差异处理。意识到这仍然包含一些噪音。

您还可以通过编程方式完成此操作。要将输出限制在当前范围内,必须实现一个包装器来创建变量。比如说

store() {
export ${1}="${*:2}"
[[ ${STORED} =~ "(^| )${1}($| )" ]] || STORED="${STORED} ${1}"
}


store VAR1 abc
store VAR2 bcd
store VAR3 cde


for i in ${STORED}; do
echo "${i}=${!i}"
done

也就是说

VAR1=abc
VAR2=bcd
VAR3=cde

尝试使用一个脚本(我们称之为“ ls _ vars”) :

  #!/bin/bash
set -a
env > /tmp/a
source $1
env > /tmp/b
diff /tmp/{a,b} | sed -ne 's/^> //p'

Chmod + x it,和:

  ls_vars your-script.sh > vars.files.save
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

这必须打印所有 shell 变量的名称。你可以得到一个列表,在源文件之前和之后,就像“设置”来区分哪些变量是新的(如其他答案所解释的)。但是请记住,这种使用 diff 的过滤可以过滤掉一些您需要但是在源文件之前已经存在的变量。

在您的例子中,如果您知道变量的名称以“ VARIABLE”开头,那么您可以源代码并执行以下操作:

for var in ${!VARIABLE@}; do
printf "%s%q\n" "$var=" "${!var}"
done

更新: 对于纯 BASH 解决方案(不使用外部命令) :

for i in _ {a..z} {A..Z}; do
for var in `eval echo "\\${!$i@}"`; do
echo $var
# you can test if $var matches some criteria and put it in the file or ignore
done
done
compgen -v

It lists all variables including local ones. I learned it from 获取名称与特定模式匹配的变量列表, and used it in 我的剧本.

从安全角度来看,无论是@akostadinov 的 answer还是@JuvenXu 的 answer,都比依赖于 set命令的非结构化输出要好,因为存在以下潜在的安全缺陷:

#!/bin/bash


function doLogic()
{
local COMMAND="${1}"
if ( set -o posix; set | grep -q '^PS1=' )
then
echo 'Script is interactive'
else
echo 'Script is NOT interactive'
fi
}


doLogic 'hello'   # Script is NOT interactive
doLogic $'\nPS1=' # Script is interactive

上面的函数 doLogic使用 set检查变量 PS1是否存在,以确定脚本是否是交互式的(不要介意这是否是实现该目标的最佳方法; 这只是一个示例)

但是,set的输出是非结构化的,这意味着任何包含换行符的变量都可能完全破坏结果。

当然,这是一个潜在的安全风险。相反,要么使用 Bash 对间接变量名扩展的支持,要么使用 compgen -v

试试这个: set | egrep "^\w+="(有或没有 | less管道)

第一个提出的解决方案,( set -o posix ; set ) | less,工程,但有一个缺点: 它传输控制代码到终端,所以他们没有正确显示。例如,如果有 (可能)一个 IFS=$' \t\n'变量,我们可以看到:

IFS='
'

取而代之。

我的 egrep解决方案正确地显示这个 (最终是其他类似的问题)

我可能已经偷了 答案不久前... ... 无论如何,作为一个功能略有不同:

    ##
# usage source bin/nps-bash-util-funcs
# doEchoVars
doEchoVars(){


# if the tmp dir does not exist
test -z ${tmp_dir} && \
export tmp_dir="$(cd "$(dirname $0)/../../.."; pwd)""/dat/log/.tmp.$$" && \
mkdir -p "$tmp_dir" && \
( set -o posix ; set )| sort >"$tmp_dir/.vars.before"




( set -o posix ; set ) | sort >"$tmp_dir/.vars.after"
cmd="$(comm -3 $tmp_dir/.vars.before $tmp_dir/.vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
echo -e "$cmd"
}

基于以上的一些答案,这个方法对我很有效:

before=$(set -o posix; set | sort);

源文件 :

comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq)

这里有一些类似于@GinkgoFr 的答案,但没有@Tino 或@DejayClayton 提出的问题, 而且比@DouglasLeeder 的 set -o posix更有活力:

+ function SOLUTION() { (set +o posix; set) | sed -ne '/^\w\+=/!q; p;'; }

区别在于这个解决方案在第一个非变量报告之后停止,例如 set报告的第一个函数

顺便说一下: “ Tino”问题解决了。即使 POSIX 被关闭,函数由 set报告, 解决方案的 sed ...部分只允许可变报告通过(例如 VAR=VALUE行)。 特别是,A2错误地将 没有输入到输出中。

+ function a() { echo $'\nA2=B'; }; A0=000; A9=999;
+ SOLUTION | grep '^A[0-9]='
A0=000
A9=999

AND: “ DejayClayton”问题得到了解决(在变量值中嵌入的换行会破坏输出——每个 VAR=VALUE得到一条输出线) :

+ A1=$'111\nA2=222'; A0=000; A9=999;
+ SOLUTION | grep '^A[0-9]='
A0=000
A1=$'111\nA2=222'
A9=999

注意:@DouglasLeeder 提供的解决方案遇到了“ DejayClayton”问题(包含换行符的值)。 下面,A1是错误的,A2根本不应该显示。

$ A1=$'111\nA2=222'; A0=000; A9=999; (set -o posix; set) | grep '^A[0-9]='
A0=000
A1='111
A2=222'
A9=999

最后: 我认为 bash的版本并不重要,但它可能重要。我在这个上面做了测试/开发:

$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)

后脚本: 考虑到其他一些对 OP 的反应,我可以100% 肯定 set 一直都是将值内的换行转换为 \n,这个解决方案依赖于 \n来避免“ DejayClayton”问题。也许这是现代人的行为?还是编译时的变体?或 set -oshopt选项设置?如果 知道这样的变化,请添加评论..。

printenv命令:

printenv打印所有 environment variables及其值。

祝你好运..。

简单的方法是使用 Bash 严格模式,在运行脚本之前设置系统环境变量,并使用 diff 对脚本的变量进行排序:

# Add this line at the top of your script :
set > /tmp/old_vars.log


# Add this line at the end of your script :
set > /tmp/new_vars.log


# Alternatively you can remove unwanted variables with grep (e.g., passwords) :
set | grep -v "PASSWORD1=\|PASSWORD2=\|PASSWORD3=" > /tmp/new_vars.log


# Now you can compare to sort variables of your script :
diff /tmp/old_vars.log /tmp/new_vars.log | grep "^>" > /tmp/script_vars.log

现在可以在/tmp/script _ vars. log 中检索脚本的变量。 或者至少是基于这个原因!

虽然有点晚了,但我还有个建议:

#!/bin/bash


set_before=$( set -o posix; set | sed -e '/^_=*/d' )


# create/set some variables
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c


set_after=$( set -o posix; unset set_before; set | sed -e '/^_=/d' )
diff  <(echo "$set_before") <(echo "$set_after") | sed -e 's/^> //' -e '/^[[:digit:]].*/d'

The Diff + sed pipeline command line outputs all script-defined variables in the desired format (as specified in the OP's post):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c

If you're only concerned with printing a list of variables with static values (i.e. expansion doesn't work in this case) then another option would be to add start and end markers to your file that tell you where your block of static variable definitions is, e.g.

#!/bin/bash


# some code


# region variables
VAR1=FOO
VAR2=BAR
# endregion


# more code

然后你就可以打印这部分文件了。

我为此准备了一些东西:

function show_configuration() {
local START_LINE=$(( $(< "$0" grep -m 1 -n "region variables" | cut -d: -f1) + 1 ))
local END_LINE=$(( $(< "$0" grep -m 1 -n "endregion" | cut -d: -f1) - 1 ))
< "$0" awk "${START_LINE} <= NR && NR <= ${END_LINE}"
}


首先,请注意,变量块位于该函数所在的同一个文件中,因此我可以使用 $0访问该文件的内容。

我使用“区域”标记来分隔代码的不同区域。因此,我简单地使用 grep作为“可变”区域标记(第一个匹配: grep -m 1) ,并使用 grep作为行号(grep -n)的前缀。然后我必须从匹配输出中剪切行号(在 :上分割)。最后,添加或减去 1,因为我不希望标记成为输出的一部分。

现在,为了打印文件的范围,我使用带有行号条件的 awk

DR

With: typeset -m <GLOBPATH>

$ VARIABLE1=abc
$ VARIABLE2=def
$ VARIABLE3=ghi
$ noglob typeset -m VARIABLE*
VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

typeset的1个文档可以在 man zshbuiltinsman zshall中找到。