Answers:
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
(或至少基于此的内容:-))
VARS="`set -o posix ; set`"; source script; SCRIPT_VARS="`grep -vFe "$VARS" <<<"$(set -o posix ; set)" | grep -v ^VARS=`"; unset VARS;
。这还将以准备保存的格式输出var。该列表将包含脚本更改的变量(取决于是否需要此变量)
source
,则应该能够使用的输出来进行declare -p
。
before=$(set -o posix; set); dosomestuff; diff <(echo "$before") <(set -o posix; set)
compgen -v
它列出了所有变量,包括局部变量。我从“ 获取名称与特定模式匹配的变量列表”中学到了它,并在脚本中使用了它。
compgen -v
还列出了未在本地设置的全局变量。不知道这是一个长期存在的错误还是所需的行为。
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
eval "printf '%q\n' $(printf ' "${!%s@}"' _ {a..z} {A..Z})"
根据上述一些答案,这对我有用:
before=$(set -o posix; set | sort);
源文件:
comm -13 <(printf %s "$before") <(set -o posix; set | sort | uniq)
如果可以进行后处理(如前所述),则可以只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
这类似于@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)
POST-SCRIPT:考虑到对OP的其他一些响应,我得到了<100%的保证,set
始终将值内的换行符转换为\n
,此解决方案将依赖该换行符来避免“ DejayClayton”问题。也许这是现代行为?还是编译时的变化?或set -o
或shopt
选项设置?如果您知道这种变化,请添加评论...
从安全角度来看,由于存在以下潜在的安全缺陷,因此@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
。
尝试以下操作:(set | egrep "^\w+="
带或不带| less
管道)
提出的第一个解决方案有效,( set -o posix ; set ) | less
但有一个缺点:它会将控制代码发送到终端,因此它们无法正确显示。因此,例如,如果有(可能)一个IFS=$' \t\n'
变量,我们可以看到:
IFS='
'
…代替。
我的egrep
解决方案正确显示了这个(并最终显示了其他类似的图像)。
bash -c $'a() { echo "\nA=B"; }; unset A; set | egrep "^\w+="' | grep ^A
显示A=B"
->失败!
我可能是前段时间偷了答案 ……反正作为功能有些许不同:
##
# 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"
}
简单的方法是在运行脚本之前通过设置系统环境变量来使用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'
的DIFF + sed的管道命令行输出在所期望的格式的所有脚本定义的变量(如在OP的交指定):
VARIABLE1=a
VARIABLE2=b
VARIABLE3=c
-o posix
现在调用diff的不错的调用将仅包含变量。