在管道中调用我的函数时未设置环境变量

And*_*rew 11 shell bash pipe io-redirection environment-variables

我有以下递归函数来设置环境变量:

function par_set {
  PAR=$1
  VAL=$2
  if [ "" != "$1" ]
  then
    export ${PAR}=${VAL}
    echo ${PAR}=${VAL}
    shift
    shift
    par_set $*
  fi
}
Run Code Online (Sandbox Code Playgroud)

如果我自己调用它,它会设置变量并回显到标准输出:

$ par_set FN WORKS
FN=WORKS
$ echo "FN = "$FN
FN = WORKS
Run Code Online (Sandbox Code Playgroud)

将标准输出重定向到文件也有效:

$ par_set REDIR WORKS > out
cat out
REDIR=WORKS
$ echo "REDIR = "$REDIR
REDIR = WORKS
Run Code Online (Sandbox Code Playgroud)

但是,如果我通过管道将 stdout 传递给另一个命令,则不会设置该变量:

$ par_set PIPE FAILS |sed -e's/FAILS/BARFS/'
PIPE=BARFS
$ echo "PIPE = "$PIPE
PIPE =
Run Code Online (Sandbox Code Playgroud)

为什么管道会阻止函数导出变量?有没有办法在不求助于临时文件或命名管道的情况下解决这个问题?

解决了:

感谢 Gilles 的工作代码:

par_set $(echo $*|tr '=' ' ') > >(sed -e's/^/  /' >> ${LOG})
Run Code Online (Sandbox Code Playgroud)

这允许这样调用脚本:

$ . ./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
Run Code Online (Sandbox Code Playgroud)

如果对完整代码感兴趣,项目托管在 bitbucket https://bitbucket.org/adalby/monitor-bash

Gil*_*il' 9

管道的每一部分(即管道的每一侧)都在一个单独的进程中运行(称为子shell,当一个shell派生一个子进程来运行部分脚本时)。在 中par_set PIPE FAILS |sed -e's/FAILS/BARFS/'PIPE变量设置在执行管道左侧的子进程中。这种变化不会反映在父进程中(环境变量不会在进程之间传递,它们只会被子进程继承。

管道的左侧总是在子壳中运行。一些 shell (ATT ksh, zsh) 在父 shell 中运行右侧;大多数也在子shell中运行右侧。

如果您想重定向脚本一部分的输出并在父 shell 中运行该部分,在 ksh/bash/zsh 中,您可以使用进程替换

par_set PROCESS SUBSTITUTION > >(sed s/ION/ED/)
Run Code Online (Sandbox Code Playgroud)

使用任何 POSIX shell,您都可以将输出重定向到命名管道。

mkfifo f
<f grep NAMED= &
par_set NAMED PIPE >f
Run Code Online (Sandbox Code Playgroud)

哦,你在变量替换周围缺少引号,你的代码在诸如par_set name 'value with spaces' star '*'.

export "${PAR}=${VAL}"
…
par_set "$@"
Run Code Online (Sandbox Code Playgroud)