set
如果不带参数调用bash内置命令,则将打印所有shell和环境变量,以及所有已定义的函数。这使得输出对于人类来说是无法使用的,并且难以实现grep
。
如何使bash内置命令set
仅打印变量而不打印函数?
还有其他仅打印shell变量而不显示功能的命令吗?
注意:bash区分shell变量和环境变量。在此处查看环境变量与bash中导出的环境变量之间的区别
set
如果不带参数调用bash内置命令,则将打印所有shell和环境变量,以及所有已定义的函数。这使得输出对于人类来说是无法使用的,并且难以实现grep
。
如何使bash内置命令set
仅打印变量而不打印函数?
还有其他仅打印shell变量而不显示功能的命令吗?
注意:bash区分shell变量和环境变量。在此处查看环境变量与bash中导出的环境变量之间的区别
Answers:
“还有其他命令仅显示shell变量,而没有功能吗?”
在man bash中,SHELL BUILTIN COMMANDS部分(在set部分中)说:“在posix模式下,仅列出了shell变量。”
(set -o posix; set)
注意:()
语法会产生一个子shell,如果您不喜欢分叉,请使用更详细的版本
set -o posix; set; set +o posix
_
,这些变量很容易摆脱:(set -o posix ; set | grep -v ^_)
set -o posix; set | sed -e '/^_/,$d'; set +o posix;
(set -o posix; set)
。如果没有括号,则当前的Bash shell的行为将更改为与Posix标准匹配。(Dunno这意味着什么,但看起来很重要。)使用括号将Bash保持不变。
POSIXLY_CORRECT=y
,并增加了posix
对$SHELLOPTS
以下是一些解决方法:
$ comm -3 <(declare | sort) <(declare -f | sort)
分解:
declare
打印每个定义的变量(是否导出)和函数。declare -f
仅打印功能。comm -3
将会删除两者共同的所有行。实际上,这将删除功能,仅保留变量。仅打印未导出的变量:
$ comm -3 <(comm -3 <(declare | sort) <(declare -f | sort)) <(env | sort)
另一个解决方法:
$ declare -p
这只会打印变量,但具有一些难看的属性。
declare -- BASH="/bin/bash"
declare -ir BASHPID=""
declare -A BASH_ALIASES='()'
declare -a BASH_ARGC='()'
...
您可以使用... cut删除属性。
$ declare -p | cut -d " " -f 3
缺点之一是IFS的值被解释而不是显示。
相比:
$ comm -3 <(declare | sort) <(declare -f | sort)
...
IFS=$' \t\n'
...
$ declare -p | cut -d " " -f 3
...
IFS="
"
...
由于"
一行中只有一行,因此很难将该输出用于进一步处理。也许可以采取一些IFS-fu来防止这种情况。
另一个解决方法,使用compgen
:
$ compgen -v
内置的bash compgen
旨在用于完成脚本中。为此,compgen -v
列出所有定义的变量。缺点:它仅列出变量名称,而不列出值。
这是一个可以列出值的技巧。
$ compgen -v | while read var; do printf "%s=%q\n" "$var" "${!var}"; done
优点:它是纯bash解决方案。缺点:由于的解释,一些值被弄乱了printf
。管道和/或循环的子外壳也添加了一些额外的变量。
declare ...| cut
变量的属性,管道会中断吗?我猜--
是在这种情况下使用的,但我仍然不安。sed
可能更安全,即declare -p | sed 's/^.* \([^ ]\+\)$/\1/'
compgen
:)
typeset
奇怪的是,内建函数具有仅显示函数(-f
)而不仅显示参数的选项。幸运的是,typeset
(或set
)在所有函数之前显示所有参数,函数和参数名称不能包含换行符或等号,并且参数值中的换行符用引号引起来(它们显示为\n
)。因此,您可以在不包含等号的第一行处停止:
set | awk -F '=' '! /^[0-9A-Z_a-z]+=/ {exit} {print $1}'
这仅显示参数名称。如果需要这些值,请更改print $1
为print $0
。
请注意,这将打印所有参数名称(此处是参数和变量的同义词),而不仅仅是环境变量(“环境变量”与“导出参数”的同义词)。
还要注意,这假设没有名称与bash对参数名称的约束不匹配的环境变量。这些变量不能在bash内部创建,但是可以在bash启动时从环境中继承:
env 'foo ()
{
=oops' bash
set | sed '/=/!Q'
我不确定如何使set
仅打印变量。但是,通过查看的输出set
,我可以得出以下似乎仅包含变量的内容:
$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+="
基本上,我正在寻找以字母,数字或标点符号开头,后跟“ =”的行。从我看到的输出中,它捕获了所有变量;但是,我怀疑这是否可移植。
如果如您的标题所示,如果您希望从此列表中减去导出的变量,从而获得未导出的变量的列表,则可以执行以下操作
$ set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars && env | sort | comm -23 ./setvars -
为了稍微分解一下,这是它的作用:
set | grep "^\([[:alnum:]]\|[[:punct:]]\)\+=" | sort > ./setvars
:这有望获得所有变量(如前所述),对其进行排序,然后将结果粘贴到文件中。&& env | sort
:上一条命令完成后,我们将调用env
并对其输出进行排序。| comm -23 ./setvars -
:最后,我们管的有序输出env
入comm
,并使用-23
选项打印所特有的第一个参数的线,在这种情况下,唯一我们从一组输出线。完成后,您可能需要清理使用命令创建的临时文件 rm ./setvars
f () {|cat <<EOF|foo=bar|EOF|}
其中|
表示换行符)。
在bash中,这将仅打印变量名称:
compgen -v
或者,如果还需要值,请使用:
declare -p
对比干净的外壳以确保rc脚本中的var也被排除在外
## get mostly local vars
diff_env(){
diff <(bash -cl 'set -o posix && set') \
<(set -o posix && set && set +o posix) | \
grep -E "^>|^\+" | \
grep -Ev "^(>|\+|\+\+) ?(BASH|COLUMNS|LINES|HIST|PPID|SHLVL|PS(1|2)|SHELL|FUNC)" | \
sed -r 's/^> ?|^\+ ?//'
}
的版本comm
太复杂了
公认的答案是伟大的。我提供了一种不涉及设置POSIX模式的替代方法:
这是我一直在使用的将函数状态存储到文件中的技术,因此我以后可以继续进行操作。第一个产生状态转储,第二个仅产生变量名列表。
set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo $a; done
set 2>/dev/null | while read a; do [[ $a == *=* ]] || break; echo ${a/=*}; done
两者都通过转储行来工作,直到找到一个没有'='字符的字符为止,这在列出第一个函数时发生。后者取消了任务。