環境変数を設定する次の再帰関数があります。
function par_set {
PAR=$1
VAL=$2
if [ "" != "$1" ]
then
export ${PAR}=${VAL}
echo ${PAR}=${VAL}
shift
shift
par_set $*
fi
}
単独で呼び出すと、変数が設定され、stdoutにエコーされます。
$ 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 =
パイプが関数が変数をエクスポートするのを防ぐのはなぜですか?一時ファイルや名前付きパイプを使用せずにこの問題を解決する方法はありますか?
解決済み:
作業コードを提供してくれたGillesに感謝します。
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
パイプの各部分(つまり、パイプの各側面)は、別々のプロセス(サブシェルと呼ばれる)で実行されます。フォークスクリプトの一部を実行する子プロセス)。では、実行パイプラインの左側にある子プロセスに変数が設定されますpar_set PIPE FAILS |sed -e's/FAILS/BARFS/'
。PIPE
この変更は親プロセスに反映されません。環境変数はプロセス間で渡されず、子プロセスからのみ継承されます。
パイプの左側は常にサブシェルで実行されます。一部のシェル(ATT ksh、zsh)は親シェルで右を実行し、ほとんどはサブシェルでも右を実行します。
スクリプトの一部の出力をリダイレクトし、親シェルでその部分を実行するには、ksh/bash/zsh で以下を使用できます。プロセスの交換。
par_set PROCESS SUBSTITUTION > >(sed s/ION/ED/)
POSIXシェルを使用すると、出力を名前付きパイプにリダイレクトできます。
mkfifo f
<f grep NAMED= &
par_set NAMED PIPE >f
ああ、そしてあなたはいなくなりました。変数置換の引用、あなたのコードは、次の問題により中止されますpar_set name 'value with spaces' star '*'
。
export "${PAR}=${VAL}"
…
par_set "$@"
答え2
パイプの各端がのサブシェルで実行され、サブシェルbash
に設定された変数がそのサブシェルにローカルであるため、これは機能しません。
修正する:
親シェルから子シェルに変数を渡すのは簡単なようですが、本物他の方法では難しいですね。いくつかの回避策には、名前付きパイプ、一時ファイル、stdoutへの書き込み、および親からの読み取りなどがあります。
いくつかの参考資料:
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
}