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


98

在我的bash脚本中,有很多变量,我必须做一些事情才能将它们保存到文件中。我的问题是如何列出在脚本中声明的所有变量并获取如下列表:

VARIABLE1=abc
VARIABLE2=def
VARIABLE3=ghi

Answers:


151

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

(或至少基于此的内容:-))


我发布时,您进行了编辑。-o posix现在调用diff的不错的调用将仅包含变量。
ezpz

8
不使用临时文件:VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;。这还将以准备保存的格式输出var。该列表将包含脚本更改的变量(取决于是否需要此变量)
ivan_pozdeev 2011年

1
@ErikAronesty如果您希望能够使用来重新创建环境source,则应该能够使用的输出来进行declare -p
2015年

2
如果要在使用diff(或任何类似命令)之前和之后比较环境而不求助于使用临时文件,则可以使用以下方法进行比较:before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
2015年

1
@Caesar不,我只尝试了非空数组。如果数组为空,则您是正确的-不打印它们。
索科维


10
for i in _ {a..z} {A..Z}; do eval "echo \${!$i@}" ; done | xargs printf "%s\n"

这必须打印所有外壳变量名称。您可以在获取文件之前和之后获取列表,就像使用“ set”来区分哪些变量是新变量一样(如其他答案所述)。但是请记住,使用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

1
+1和oneliner版本(带有单个评估):eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
netj

4

根据上述一些答案,这对我有用:

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

源文件

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

3

如果可以进行后处理(如前所述),则可以只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

2

这类似于@GinkgoFr的答案,但是没有@Tino或@DejayClayton识别的问题,并且比@DouglasLeeder的聪明set -o posix点更强大:

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

区别在于,此解决方案在第一个非变量报告(例如,由 set

顺便说一句:解决了“ Tino”问题。即使POSIX已关闭并且功能由报告setsed ...该解决方案的一部分也仅允许变量报告通过(例如,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)

POST-SCRIPT:考虑到对OP的其他一些响应,我得到了<100%的保证,set 始终将值内的换行符转换为\n,此解决方案将依赖该换行符来避免“ DejayClayton”问题。也许这是现代行为?还是编译时的变化?或set -oshopt选项设置?如果知道这种变化,请添加评论...


0

尝试使用脚本(将其称为“ ls_vars”):

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

chmod + x它,然后:

  ls_vars your-script.sh > vars.files.save

0

从安全角度来看,由于存在以下潜在的安全缺陷,因此@akostadinov的答案或@JuvenXu的答案比依赖于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


0

尝试以下操作:(set | egrep "^\w+="带或不带| less管道)

提出的第一个解决方案有效,( set -o posix ; set ) | less但有一个缺点:它会将控制代码发送到终端,因此它们无法正确显示。因此,例如,如果有(可能)一个IFS=$' \t\n'变量,我们可以看到:

IFS='
'

…代替。

我的egrep解决方案正确显示了这个(并最终显示了其他类似的图像)


已经过去了8年
lauriys

拒绝投票,因为它是错误的 bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A显示A=B"->失败!
Tino

奇怪的测试似乎仅暗示“ _”(前一个命令的最后一个参数)伪变量,但在其他变量(尤其是IFS)上不会失败。在“ bash -c”下运行它也可能使其不重要。您至少应该保持中立而不是低估。
GingkoFr

0

我可能是前段时间偷了答案 ……反正作为功能有些许不同:

    ##
    # 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"
    } 

0

printenv命令:

printenv打印所有内容environment variables及其值。

祝好运...


0

简单的方法是在运行脚本之前通过设置系统环境变量来使用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中检索脚本的变量。或至少基于此!


0

派对晚了一点,但这是另一个建议:

#!/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'

DIFF + sed的管道命令行输出在所期望的格式的所有脚本定义的变量(如在OP的交指定):

VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.