我有以下递归函数来设置环境变量:
function par_set {
PAR=$1
VAL=$2
if [ "" != "$1" ]
then
export ${PAR}=${VAL}
echo ${PAR}=${VAL}
shift
shift
par_set $*
fi
}
如果我单独调用它,它会设置变量并回显到标准输出:
$ par_set FN WORKS
FN=WORKS
$ echo "FN = "$FN
FN = WORKS
将 stdout 重定向到文件也可以:
$ par_set REDIR WORKS > out
cat out
REDIR=WORKS
$ echo "REDIR = "$REDIR
REDIR = WORKS
但是,如果我将 stdout 通过管道传输到另一个命令,则该变量不会被设置:
$ par_set PIPE FAILS |sed -e's/FAILS/BARFS/'
PIPE=BARFS
$ echo "PIPE = "$PIPE
PIPE =
为什么管道会阻止函数导出变量?有没有办法解决这个问题,而无需求助于临时文件或命名管道?
解决了:
感谢吉尔斯的工作代码:
par_set $(echo $*|tr '=' ' ') > >(sed -e's/^/ /' >> ${LOG})
这允许这样调用脚本:
$ . ./script.sh PROCESS_SUB ROCKS PIPELINES=NOGOOD
$ echo $PROCESS_SUB
ROCKS
$ echo $PIPELINES
NOGOOD
$ cat log
7:20140606155622162731431:script.sh:29581:Parse Command Line parameters. Params must be in matched pairs separated by one or more '=' or ' '.
PROCESS_SUB=ROCKS
PIPELINES=NOGOOD
项目托管在 Bitbucket 上https://bitbucket.org/adalby/monitor-bash如果对完整代码感兴趣。
答案1
管道的每个部分(即管道的每一侧)都在单独的进程(称为子shell,当 shell叉子运行部分脚本的子进程)。在 中par_set PIPE FAILS |sed -e's/FAILS/BARFS/'
,PIPE
变量在执行管道左侧的子进程中设置。这种变化不会反映在父进程中(环境变量不会在进程之间传递,它们仅由子进程继承。
管道的左侧始终在子 shell 中运行。某些 shell(ATT ksh、zsh)在父 shell 中运行右侧;大多数还在子 shell 中运行右侧。
如果您想重定向脚本一部分的输出并在父 shell 中运行该部分,在 ksh/bash/zsh 中,您可以使用流程替代。
par_set PROCESS SUBSTITUTION > >(sed s/ION/ED/)
使用任何 POSIX shell,您可以将输出重定向到命名管道。
mkfifo f
<f grep NAMED= &
par_set NAMED PIPE >f
哦,还有你失踪了围绕变量替换的引号,你的代码会在诸如 之类的事情上中断par_set name 'value with spaces' star '*'
。
export "${PAR}=${VAL}"
…
par_set "$@"
答案2
这是行不通的,因为管道的每一端都在 中的子 shell 中运行bash
,并且子 shell 中设置的变量对于该子 shell 来说是本地的。
更新:
看起来将变量从父 shell 传递到子 shell 很容易,但是真的很难用其他方式做到这一点。一些解决方法是命名管道、临时文件、写入标准输出并在父级中读取等。
一些参考:
http://mywiki.wooledge.org/BashFAQ/024
https://stackoverflow.com/q/15541321/3565972
https://stackoverflow.com/a/15383353/3565972
http://forums.opensuse.org/showthread.php/458979-How-export-variable-in-subshell-back-out-to-parent
答案3
您指出了子外壳 - 可以通过管道外外壳中的一些欺骗来解决 - 但问题的更难部分与管道的并发性。
管道的所有进程成员开始于一次,所以如果你这样看问题可能会更容易理解:
{ sleep 1 ; echo $((f=1+2)) >&2 ; } | echo $((f))
###OUTPUT
0
...
3
管道进程无法继承变量值,因为它们在设置变量之前就已经关闭并运行。
我真的无法理解你的功能的意义是什么 - 它有什么目的,但export
还没有?或者只是var=val
?例如,这里是几乎再次相同的管道:
pipeline() {
{ sleep 1
echo "f=$((f=f+1+2))" >&3
} | echo $((f)) >&2
} 3>&1
f=4 pipeline
###OUTPUT
4
...
f=7
与export
:
export $(f=4 pipeline) ; pipeline
###OUTPUT:
4
7
...
f=10
所以你的事情可以这样工作:
par_set $(echo PIPE FAILS |
sed 's/FAIL/WORK/;/./w /path/to/log')
它将记录到文件sed
的输出并将其作为 shell-split 传递给您的函数"$@"
。
或者,另一种选择:
$ export $(echo PIPE FAILS | sed 's/ FAIL/=WORK/')
$ par_set $PIPE TWICE_REMOVED
$ echo "WORKS = "$WORKS
WORKS = TWICE_REMOVED
不过,如果我要编写你的函数,它可能看起来像这样:
_env_eval() {
while ${2+:} false ; do
${1:+export} ${1%%["${IFS}"]*}="${2}" || :
shift 2
done
}