让我们假设一些bash外壳脚本喜欢:
#!/bin/bash
a=3
b=7
set -o posix
set
在这种情况下,执行变量set
或其他一些命令打印环境变量( env
, declare
... 等) 输出 mya
和b
变量,加上一个很多变数(和功能)来自环境(PATH
,PWD
,TEMP
...等)。
有没有办法只打印那些已经存在的变量在脚本期间定义身体?
答案1
您可以将变量列表存储在脚本开头,并将其与脚本中某处的值进行比较。请注意,类似命令的输出set
并不是明确的:类似命令set | sed 's/=.*//'
不适用于值包含换行符的变量。 (在 bash 中,它实际上适用于字符串变量,但不适用于数组,并且它还显示函数代码。)我认为以下代码片段可靠地列出了当前定义的变量(按字母顺序排列):
variables=$(tmp=$(declare -p +F); declare () { echo "${2%%=*}"; }; eval "$tmp")
因此,initial_variables=…
在脚本的开头设置,并与后面的值进行比较。除了echo
直接对列表进行操作之外,您还可以使用其他方法。
initial_variables=" $(tmp=$(declare -p +F); declare () { echo "${2%%=*}"; }; eval "$tmp") "
…
( tmp=$(declare -p +F)
declare () {
case "$initial_variables" in
*" $2 "*) :;; # this variable was present initially
*) eval "set -- \"\$$2\" \"\$2\""; echo "locally defined $2=$1";;
esac
}
)
答案2
我作弊了:我只是将不想列出的变量重命名为以字母“aaa”开头的变量(例如我从另一个文件中获取的变量),然后调用内置命令compgen -v
。然后我按数字我知道脚本中存在的变量。
例如,我从另一个文件中获取了两个以 aaa 开头的变量,并且我的脚本中有三个随机命名的变量(首字母范围从从头到尾),通过将这个简单的命令放入我的脚本中,我可以在输出中仅列出这三个变量:
compgen -v | tail -n 3
编辑:
如果我不知道脚本中有多少个变量:那么我可以这样做:
compgen -v | awk '$0 == "aaauser" {i=1;next};i && i++ <= 100'
在哪里AAA用户是个最后按字母顺序排列我从其他文件中获取的变量。您可以将其应用于任何已知变量。
答案3
comm
使用,和过程替换的2 行策略compgen -v
...
ENVVARS="$(compgen -v)"
:
<script>
:
comm -1 -3 <(sort <<< "$ENVVARS") <(compgen -v | sort)
从comm --help
...
Usage: comm [OPTION]... FILE1 FILE2
Compare sorted files FILE1 and FILE2 line by line.
With no options, produce three-column output. Column one contains
lines unique to FILE1, column two contains lines unique to FILE2, and
column three contains lines common to both files.
-1 suppress lines unique to FILE1
-2 suppress lines unique to FILE2
-3 suppress lines that appear in both files
该comm
命令允许我们从脚本末尾的总环境变量集中“减去”传递给脚本的环境变量集,只留下脚本创建的环境变量。
选项 -1 和 -3 丢弃除此脚本中创建的变量(即“文件 2”中的行)之外的所有变量。 Bash Process Substitution<(...)
用于列出变量并对其进行排序,然后将输出视为文件。
请注意,不包括声明为函数本地的变量,因为它们超出了主脚本的范围。
可以使用相同的技术来列出函数内创建的变量。然而,在这种情况下,命名此类变量以下划线开头(_)
并在调用中添加下划线前缀的约定compgen
可能更理想......
LOCALVARS="$(compgen -v _)"
答案4
这是获取所有当前定义的变量名的排序列表的便携式方法:
v(){ set "${IFS+IFS=\$2}" "$IFS"; unset IFS
set $(set|sed -n '/^[_[:alpha:]][[:alnum:]]*=/s/=.*//p'|sort -u) "$@"
while [ "$#" -gt 2 ]
do eval '[ "${'$1'+1}" = 1 ]' &&
echo "$1"; shift
done; eval "$1"
}
该列表被echo
编辑到v()
的标准输出 - 每行一个名称。因为它是sort
ed 的,所以它是以后与类似生成的列表进行比较的良好候选者,就像您可能使用comm
或类似的那样。该列表有意排除$IFS
- 这是因为 - 设置或未设置 -$IFS
是总是实际上,因此必须在这样的列表中给出。可以这样改变:
set "${IFS+IFS=\$2}" "$IFS"; IFS='
'; set $(set|...
...但那只会永远包括无论是否设置,它都在列表中 - 这让我回到了之前的观点......