Tüm değişkenler bir kabuk betiğinden diğerine aktarılsın mı?


193

Diyelim ki adlı bir kabuk / bash betiği test.shvar:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Benim test2.shşöyle görünüyor:

#!/bin/bash

echo ${TESTVARIABLE}

Bu çalışmıyor. Imho bu overkill olduğundan tüm değişkenleri parametre olarak geçirmek istemiyorum.

Farklı bir yol var mı?


Bir fikir, değişkenleri bir dosyaya kaydetmek ve daha sonra başka bir komut dosyasına yüklemek.
Rodrigo

@Rodrigo Daha önce benzer bir yaklaşımı, bir betik çalışması arasında değerleri bir miktar başarıyla "kaydetmek" için kullandım. Akılda tutulması gereken bir şey, eğer komut dosyalarının birden fazla örneği çalıştırılırsa, işler zorlaşabilir. Ancak bunları bash atama formuna kaydederseniz, değişkenleri içe aktarmak için dosyayı kaynaklayabilirsiniz
FatalError

Yanıtlar:


266

Temel olarak iki seçeneğiniz vardır:

  1. export TESTVARIABLE2. betiği çalıştırmadan önce değişkeni bir ortam değişkeni ( ) yapın.
  2. 2. komut dosyasını kaynaklayın, yani . test2.shaynı kabukta çalışacaktır. Bu, diziler gibi daha karmaşık değişkenleri kolayca paylaşmanıza izin verir, ancak diğer komut dosyasının kaynak kabuğundaki değişkenleri değiştirebileceği anlamına gelir.

GÜNCELLEME:

exportBir ortam değişkeni ayarlamak için kullanmak için, varolan bir değişkeni kullanabilirsiniz:

A=10
# ...
export A

Bu, hem çalışmak gerektiğini bashve sh. bashayrıca şu şekilde birleştirilmesine izin verir:

export A=10

Bu benim de çalışır sh(ki olur bash, echo $SHELLkontrol etmek için kullanabilirsiniz ). Ama bunun hepsinde çalışacağına inanmıyorum, bu shyüzden güvenli oynamak ve onları ayırmak en iyisidir.

Bu şekilde dışa aktardığınız değişkenler yürüttüğünüz komut dosyalarında görünür olacaktır, örneğin:

kül:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Sonra:

$ ./a.sh
The message is: hello

Bunların her ikisi de kabuk komut dosyaları olması da sadece rastlantısaldır. Ortam değişkenleri yürüttüğünüz herhangi bir işleme geçirilebilir, örneğin python kullandıysak şöyle görünebilir:

kül:

#!/bin/sh

MESSAGE="hello"
export MESSAGE
./b.py

b.py:

#!/usr/bin/python

import os

print 'The message is:', os.environ['MESSAGE']

kaynak sağlama:

Bunun yerine şöyle kaynak yapabiliriz:

kül:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Sonra:

$ ./a.sh
The message is: hello

Bu, az çok b.shdoğrudan içeriği alır ve aynı kabukta yürütür . Değişkene erişmek için dışa aktarmamız gerekmediğine dikkat edin. Bu, sahip olduğunuz tüm değişkenleri dolaylı olarak paylaşır ve diğer komut dosyasının kabuktaki değişkenleri eklemesine / silmesine / değiştirmesine izin verir. Tabii ki, bu modelde her iki komut dosyanız da aynı dilde ( shveya bash) olmalıdır. İletileri ileri geri nasıl iletebileceğimize bir örnek vermek için:

kül:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

echo "[A] The message is: $MESSAGE"

b.sh:

#!/bin/sh

echo "[B] The message is: $MESSAGE"

MESSAGE="goodbye"

Sonra:

$ ./a.sh
[B] The message is: hello
[A] The message is: goodbye

Bu eşit derecede iyi çalışıyor bash. Ayrıca, diziler veya ilişkilendirilebilir diziler gibi bir ortam değişkeni olarak ifade edemediğiniz (en azından sizin parçanızda ağır bir kaldırma olmadan) daha karmaşık verileri paylaşmanızı kolaylaştırır.


1
Alt kabuğa 1 $ iletmem gerekirse ne olur ('sudo sh -c ...' bir komut dosyasından çağrılır)? Bir ortam değişkenine $ 1 eklemeliyim, dışa aktarmalı ve komuttaki değişkeni kullanmalı mıyım?
Urhixidur

Sadece sudo erişim haklarına ihtiyacınız varsa çevre değişkenlerini korumak için sudo -E ... kullanabilirsiniz.
yaş

2
@FatalError ". ./B.sh" adını verdiğiniz son sabahtaki büyüyü açıklar mısınız? İlk noktanın anlamı nedir? Harika btw çalışır!
Deian

Ayrıca değişkenleri ve değerleri bir dosyaya ayarlayabilir ve bu şekilde paylaşabilirsiniz
newshorts 27:18

@Deian, Dönem (nokta) "kaynak" inşa bash için kısa el
Kirill Kost

27

Önemli Hata doğrudan bir olasılık verdi: ikinci betiğinizi kaynaklayın! bu ikinci komut dosyasının bazı değerli değişkenlerinizi değiştirebileceğinden endişe ediyorsanız, bunu her zaman bir alt kabukta kaynaklayabilirsiniz:

( . ./test2.sh )

Parantezler, kaynağın alt kabukta gerçekleşmesini sağlar, böylece üst kabuk değişikliklerin test2.shgerçekleştirilebileceğini görmez .


Burada kesinlikle belirtilmesi gereken başka bir olasılık daha var: kullanım set -a.

Gönderen POSIX setreferans :

-a: Bu seçenek açıkken, atamanın gerçekleştirildiği her değişken için dışa aktarma özelliği ayarlanacaktır; bkz. IEEE Std 1003.1-2001, Kısım 4.21, Değişken Atama . Atama önceyse bir komut bir yardımcı ad, ihracat dahili araçları özel profilli önceki bir neden olduğuna nitelik dışında, yarar tamamlanıncaya sonra geçerli çalıştırma ortamı inat etmeyecektir ihracat almanın doğasında sonra devam etmek niteliği tamamlandı. Atama, komutta bir yardımcı program adından önce gelmiyorsa veya atama, getopts veya read işleminin bir sonucuysa Değişken ayarlanana kadar export özelliği devam edecektir.

Gönderen Bash Manuel :

-a: Sonraki komutların ortamına dışa aktarmak için değiştirilen veya oluşturulan değişkenleri ve işlevi işaretleyin.

Yani sizin durumunuzda:

set -a
TESTVARIABLE=hellohelloheloo
# ...
# Here put all the variables that will be marked for export
# and that will be available from within test2 (and all other commands).
# If test2 modifies the variables, the modifications will never be
# seen in the present script!
set +a

./test2.sh

 # Here, even if test2 modifies TESTVARIABLE, you'll still have
 # TESTVARIABLE=hellohelloheloo

Özelliklerin yalnızca set -adeğişkenle dışa aktarma için işaretlendiğini belirttiğine dikkat edin . Yani:

set -a
a=b
set +a
a=c
bash -c 'echo "$a"'

cboş bir satır yankılamaz , ne de b(yani set +adışa aktarma için işareti kaldırılmaz veya atamanın değerini yalnızca dışa aktarılan ortam için “kaydetmez”). Bu elbette en doğal davranıştır.

Sonuç: set -a/ kullanmak set +a, tüm değişkenleri manuel olarak dışa aktarmaktan daha az sıkıcı olabilir. Yalnızca aynı kabuk dilinde yazılmış komutlar için değil, herhangi bir komut için çalışacağı için ikinci komut dosyasını kaynaklamaktan daha üstündür.


18

Aslında, tekrar dışa aktarma ve ayarlamayı kaldırma veya kaynak sağlama işleminden daha kolay bir yol var (en azından bash'de, ortam değişkenlerini manuel olarak geçirmeyi tamamladığınız sürece):

a.sh olsun

#!/bin/bash
secret="winkle my tinkle"
echo Yo, lemme tell you \"$secret\", b.sh!
Message=$secret ./b.sh

ve b.sh be

#!/bin/bash
echo I heard \"$Message\", yo

Gözlenen çıktı

[rob @ Archie testi] $ ./a.sh
Yo, sana "çıngırtıma göz kırp" demiştim, b.sh!
"Göz kırpışımı kırıştın" duydum

Son satırında sihirli yalanlar a.sh, Messageçağırma sadece süresince, ./b.sh, değerine ayarlanır secretdan a.sh. Temel olarak, adlandırılmış parametreler / argümanlar gibi biraz. Daha da fazlası, $DISPLAYbir uygulamanın hangi X Server'da başladığını kontrol eden gibi değişkenler için bile çalışır .

Unutmayın, ortam değişkenleri listesinin uzunluğu sonsuz değildir. Nispeten vanilya çekirdeği olan sistemimde, xargs --show-limitstamponun maksimum boyutunun 2094486 bayt olduğunu söylüyor. Teorik olarak, verileriniz bundan daha büyükse kabuk komut dosyalarını yanlış kullanıyorsunuz (kanallar, herhangi biri?)


7

Önemli Hata cevabına ek olarak, değişkenleri başka bir kabuk betiğine geçirmenin bir yolu daha vardır.

Yukarıda önerilen çözümün bazı dezavantajları vardır:

  1. using Export : İyi bir tasarım uygulaması olmayan değişkenin kapsamı dışında bulunmasına neden olacaktır.
  2. using Source : Başka bir kabuk kaynağı olan başka bir kabuk komut dosyasında önceden tanımlanmış bir değişkenin ad çakışmalarına veya yanlışlıkla üzerine yazılmasına neden olabilir.

Kullanabileceğimiz başka bir basit çözüm daha var. Gönderdiğiniz örneği göz önünde bulundurarak,

test.sh

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh "$TESTVARIABLE"

test2.sh

#!/bin/bash

echo $1

çıktı

hellohelloheloo

Ayrıca, çok ""kelimeli dizeler geçirirsek gerekli olduğunu da belirtmek önemlidir . Bir örnek daha alalım

master.sh

#!/bin/bash
echo in master.sh
var1="hello world"
sh slave1.sh $var1
sh slave2.sh "$var1"
echo back to master

slave1.sh

#!/bin/bash
echo in slave1.sh
echo value :$1

slave2.sh

#!/bin/bash
echo in slave2.sh
echo value : $1

çıktı

in master.sh
in slave1.sh
value :"hello
in slave2.sh
value :"hello world"

Bu bağlantıda uygun bir şekilde açıklanan nedenlerden dolayı olur


6
kullanmak sourceaslında iyi bir şeydir. Endişenizle ilgili olarak: önceden tanımlanmış bir değişkenin yanlışlıkla üzerine yazılması , her zaman bir alt kabukta kaynak yapabilirsiniz. Bu, sorunu tamamen çözer.
gniourf_gniourf

@gniourf_gniourf bir alt kabuğa nasıl kaynak verilir?
Toskan

@Toskan Bu cevabım söz edilir: ( . ./test2.sh ). Parantezler Bash'in içeriğini alt kabukta çalıştırmasını sağlar.
gniourf_gniourf

7

Bash'te, değişkeni gösterildiği gibi parantez kullanarak bir alt kabuk içinde dışa aktarırsanız, dışa aktarılan değişkenleri sızdırmazsınız:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
(
export TESTVARIABLE    
source ./test2.sh
)

Buradaki avantaj, komut dosyasını komut satırından çalıştırdıktan sonra, $ TESTVARIABLE ortamınıza sızdığını görmemeniz:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$

Ben kullandım ama işe yaramıyor gibi yazdım: #! / Bin / bash için ((i = 1; i <= 3; i ++)) yapmak (ihracat i kaynağı ./script2.sh) HATA yaptı diyor ki: ./script2.sh:Böyle bir dosya veya dizin yok
Pranjal Gupta

En üst düzey ortam (komut satırı $istemi) dışa aktarılanları asla görmeyeceğinden alt kabuk aslında gerekli değildir $TESTVARIABLE. Dışa aktarma, yalnızca değişkenin bir kopyasını sonraki alt işlemlere geçirir . Değeri depolamaya kaydetmek ve bu depolamayı daha sonra üst işlem komut dosyasında okumak dışında değişkenleri zinciri üst süreçlere yedeklemek mümkün değildir. Üst komut dosyasının ikinci bir kopyasını oluşturmak mümkündür, ancak bu aynı işlem olmaz . Mükemmel bir açıklama burada bulunabilir .
DocSalvager

2

Başka bir seçenek kullanıyor eval. Bu yalnızca dizgilere güveniliyorsa uygundur. İlk komut dosyası değişken atamalarını yansıtabilir:

echo "VAR=myvalue"

Sonra:

eval $(./first.sh) ./second.sh

Olduğu için ikinci komut set ortam değişkenleri istediğinizde bu yaklaşım özel ilgi değil bash ve ayrıca istemiyorum exportonlar duyarlıdır belki, değişkenler ve bunları kalıcı istemiyoruz.


1

Benim için biraz daha kolay olan başka bir yol da adlandırılmış boruları kullanmaktır. Adlandırılmış kanallar, farklı işlemler arasında senkronizasyon ve mesaj gönderme yolu sağladı.

A.bash:

#!/bin/bash
msg="The Message"
echo $msg > A.pipe

B.bash:

#!/bin/bash
msg=`cat ./A.pipe`
echo "message from A : $msg"

Kullanımı:

$ mkfifo A.pipe #You have to create it once
$ ./A.bash & ./B.bash # you have to run your scripts at the same time

B.bash mesaj bekleyecek ve A.bash mesajı gönderir göndermez B.bash çalışmalarına devam edecektir.

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.