解决了:

解决了:

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

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-subshel​​l-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
}

相关内容