Bir boru hattında işlevim çağrıldığında ortam değişkenleri ayarlanmadı


10

Ortam değişkenlerini ayarlamak için aşağıdaki özyinelemeli işlev var:

function par_set {
  PAR=$1
  VAL=$2
  if [ "" != "$1" ]
  then
    export ${PAR}=${VAL}
    echo ${PAR}=${VAL}
    shift
    shift
    par_set $*
  fi
}

Tek başına çağırırsam, hem değişkeni hem de yankıları stdout'a ayarlar:

$ par_set FN WORKS
FN=WORKS
$ echo "FN = "$FN
FN = WORKS

Stdout'u bir dosyaya yeniden yönlendirmek de işe yarar:

$ par_set REDIR WORKS > out
cat out
REDIR=WORKS
$ echo "REDIR = "$REDIR
REDIR = WORKS

Ancak, stdout'u başka bir komuta bağlarsam, değişken ayarlanmaz:

$ par_set PIPE FAILS |sed -e's/FAILS/BARFS/'
PIPE=BARFS
$ echo "PIPE = "$PIPE
PIPE =

Boru neden işlevin değişkeni dışa aktarmasını engelliyor? Geçici dosyalara veya adlandırılmış yöneltmelere başvurmadan bunu düzeltmenin bir yolu var mı?

çözüldü:

Gilles sayesinde çalışma kodu:

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

Bu, betiğin bu şekilde çağrılmasını sağlar:

$ . ./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

Proje , tam kodla ilgileniyorsa bitbucket https://bitbucket.org/adalby/monitor-bash adresinde barındırıldı .

Yanıtlar:


8

(Diğer bir deyişle, borunun her iki tarafında) bir boru hattı her bir bölümü (bir kabuk, bir alt kabuk adı verilen, ayrı bir işlemde çalışır çatallar komut parçasını çalıştırmak için bir alt işlemi). İçinde par_set PIPE FAILS |sed -e's/FAILS/BARFS/', PIPEdeğişken borunun sol tarafını çalıştıran alt süreçte ayarlanır. Bu değişiklik üst sürece yansıtılmaz (ortam değişkenleri işlemler arasında aktarılmaz, yalnızca alt işlemler tarafından devralınır.

Bir borunun sol tarafı her zaman bir alt kabukta çalışır. Bazı kabuklar (ATT ksh, zsh) ana kabuklarda sağ tarafı çalıştırır; çoğu da sağ tarafı bir alt kabukta çalıştırır.

Komut dosyasının bir bölümünün çıktısını yeniden yönlendirmek ve o bölümü üst kabukta çalıştırmak istiyorsanız, ksh / bash / zsh içinde işlem ikamesini kullanabilirsiniz .

par_set PROCESS SUBSTITUTION > >(sed s/ION/ED/)

Herhangi bir POSIX kabuğunda, çıktıyı adlandırılmış bir boruya yeniden yönlendirebilirsiniz.

mkfifo f
<f grep NAMED= &
par_set NAMED PIPE >f

Oh, ve değişken ikamelerle ilgili alıntıları kaçırıyorsunuz , kodunuz gibi şeyleri kırıyor par_set name 'value with spaces' star '*'.

export "${PAR}=${VAL}"

par_set "$@"

Kazanmak için süreç ikamesi! Adlandırılmış bir boru veya geçici dosya kullanabileceğimi biliyordum, ama bunlar çirkin, zayıf eşzamanlılığa sahip ve komut dosyası ölürse bir karışıklık bırakıyor (tuzak sonuncusuna yardımcı oluyor). Uzay meselesi kasıtlı. Kural olarak, komut satırına iletilen değişkenler ad / değer çiftindedir ve '=' ve / veya '' ile ayrılır.
Andrew

3

Bu, borunun her iki tarafında bir alt kabukta çalıştığı ve bir alt kabukta bashayarlanan değişkenler o alt kabukta yerel olduğu için çalışmaz.

Güncelleme:

Değişkenleri üst öğeden alt kabuğa geçirmek kolay gibi görünüyor, ancak bunu başka şekilde yapmak gerçekten zor. Bazı geçici çözümler boru, geçici dosyalar, stdout'a yazma ve üst öğede okuma vb. Olarak adlandırılır.

Bazı referanslar:

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-Nasıl-ihracat-değişken-in-alt kabuğa geri aşımı-to-ebeveyn


Bunun bir olasılık olduğunu düşündüm, ancak fonksiyonun mevcut kabukta yürütülmesi gerekiyor. Ben fonksiyona bir "echo $$" ekleyerek test. par_set STILL FAILS | sed -e "s / ^ / sedpid = $$, fnpid = /" çıkışlar sedpid = 15957, fnpid = 15957.
Andrew

@Andrew Bu stackoverflow.com/a/20726041/3565972 adresine bakın . Görünüşe göre, $$hem ebeveyn hem de çocuk kabukları için aynıdır. $BASHPIDAlt kabuk pidini almak için kullanabilirsiniz . Ne zaman echo $$ $BASHPIDiçinde par_setfarklı pids olsun.
savanto

@Andrew Hala bir çözüm bulmaya çalışıyor, ancak başarısız oluyor! =)
savanto

Savanto-teşekkür @. $$ vs $ BASHPID veya boru zorlamak subshells hakkında bilmiyordum.
Andrew

0

Alt kabukları işaret edersiniz - bu, bir boru hattının dışındaki kabukta bir kaçma ile çözülebilir - ancak sorunun daha zor kısmı, boru hattının eşzamanlılığıyla ilgilidir .

Boru hattının tüm işlem üyeleri bir kerede başlar ve bu nedenle şu şekilde bakarsanız sorunun anlaşılması daha kolay olabilir:

{ sleep 1 ; echo $((f=1+2)) >&2 ; } | echo $((f))
###OUTPUT
0
...
3

Boru hattı işlemleri, değişken ayarlanmadan önce zaten kapalı ve çalışıyor oldukları için değişken değerlerini devralamaz.

İşlevin amacının ne olduğunu gerçekten anlayamıyorum - exportzaten hangi amaca hizmet etmiyor? Hatta sadece var=val? Örneğin, yine neredeyse aynı boru hattı:

pipeline() { 
    { sleep 1
      echo "f=$((f=f+1+2))" >&3
    } | echo $((f)) >&2
} 3>&1

f=4 pipeline

###OUTPUT

4
...
f=7

Ve ile export:

export $(f=4 pipeline) ; pipeline

###OUTPUT:

4
7
...
f=10

Böylece işiniz şöyle çalışabilir:

par_set $(echo PIPE FAILS | 
    sed 's/FAIL/WORK/;/./w /path/to/log')

Bu da bir dosyanın sedçıktısında oturum açar ve onu kabuk bölme işlevi olarak teslim eder "$@".

Veya alternatif olarak:

$ export $(echo PIPE FAILS | sed 's/ FAIL/=WORK/')
$ par_set $PIPE TWICE_REMOVED
$ echo "WORKS = "$WORKS
WORKS = TWICE_REMOVED

Ben senin fonksiyonunu yazacak olsaydım, muhtemelen şöyle görünecektir:

_env_eval() { 
    while ${2+:} false ; do
       ${1:+export} ${1%%["${IFS}"]*}="${2}" || :
       shift 2
    done
}

Bu doğru, ama alakasız: Andrew değişkenleri boru hattı bittikten sonra kullanmaya çalışıyor, borunun diğer tarafında değil.
Gilles 'SO- kötü olmayı bırak'

@Gilles - bunu yapabilir. |pipeline | sh
mikeserv

@Gilles - aslında ihtiyacınız bile yok sh. O zaten kullanıyor export.
mikeserv

Genel amaç sistem izleme komut dosyalarıdır. Onları etkileşimli olarak, diğer komut dosyalarından veya cron'dan çağırabilme. Env değişkenlerini ayarlayarak, komut satırını veya yapılandırma dosyasını geçirerek parametreleri iletmek ister. Bir veya daha fazla kaynaktan girdi alma ve ortamı doğru ayarlama işlevi vardır. Ancak, ben de isteğe bağlı olarak çıkış (biçime sed geçtikten sonra), dolayısıyla boru ihtiyacı günlüğü elde etmek istiyorum.
Andrew

1
@ mikeserv- Tüm yorumlar için teşekkürler. Gilles'in süreç ikamesi önerisiyle gittim, ancak kodum için tartışmak zorunda kalmam beni daha iyi bir programcı yapıyor.
Andrew
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.