Pass semua variabel dari satu skrip shell ke yang lain?

193

Katakanlah saya memiliki skrip shell / bash test.shdengan nama :

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

test2.shPenampilan saya seperti ini:

#!/bin/bash

echo ${TESTVARIABLE}

Ini tidak bekerja. Saya tidak ingin meneruskan semua variabel sebagai parameter karena imho ini berlebihan.

Apakah ada cara yang berbeda?

Toskan
sumber
Satu ide adalah menyimpan variabel dalam file dan kemudian memuatnya di skrip lain.
Rodrigo
@Rodrigo Saya telah menggunakan pendekatan yang sama sebelumnya untuk "menyimpan" nilai antara menjalankan skrip dengan beberapa keberhasilan. Hanya satu hal yang perlu diingat, adalah bahwa jika beberapa contoh skrip dijalankan, hal-hal mungkin menjadi tidak pasti. Tetapi jika Anda menyimpannya dalam formulir penugasan bash, Anda dapat mengambil sumber file untuk mengimpor variabel
FatalError

Jawaban:

266

Pada dasarnya Anda memiliki dua opsi:

  1. Jadikan variabel sebagai variabel lingkungan ( export TESTVARIABLE) sebelum menjalankan skrip ke-2.
  2. Sumber skrip ke-2, yaitu . test2.shdan akan berjalan di shell yang sama. Ini akan memungkinkan Anda berbagi variabel yang lebih kompleks seperti array dengan mudah, tetapi juga berarti bahwa skrip lain dapat memodifikasi variabel dalam shell sumber.

MEMPERBARUI:

Untuk menggunakan exportuntuk mengatur variabel lingkungan, Anda dapat menggunakan variabel yang sudah ada:

A=10
# ...
export A

Ini harus bekerja di keduanya bashdan sh. bashjuga memungkinkan untuk digabungkan seperti:

export A=10

Ini juga berfungsi di saya sh (yang kebetulan bash, dapat Anda gunakan echo $SHELLuntuk memeriksa). Tapi saya tidak percaya itu dijamin akan berhasil sh, jadi sebaiknya mainkan dengan aman dan pisahkan.

Setiap variabel yang Anda ekspor dengan cara ini akan terlihat dalam skrip yang Anda jalankan, misalnya:

Abu:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Kemudian:

$ ./a.sh
The message is: hello

Fakta bahwa keduanya adalah skrip shell juga hanya kebetulan. Variabel lingkungan dapat diteruskan ke proses apa pun yang Anda jalankan, misalnya jika kami menggunakan python, akan terlihat seperti:

Abu:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Sumber:

Sebaliknya, kita dapat sumber seperti ini:

Abu:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

Kemudian:

$ ./a.sh
The message is: hello

Ini lebih atau kurang "mengimpor" konten b.shsecara langsung dan mengeksekusi di shell yang sama . Perhatikan bahwa kami tidak perlu mengekspor variabel untuk mengaksesnya. Ini secara implisit membagikan semua variabel yang Anda miliki, serta memungkinkan skrip lain untuk menambah / menghapus / memodifikasi variabel dalam shell. Tentu saja, dalam model ini kedua skrip Anda harus bahasa yang sama ( shatau bash). Untuk memberi contoh bagaimana kita bisa meneruskan pesan bolak-balik:

Abu:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

Kemudian:

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

Ini berfungsi sama baiknya di bash. Ini juga memudahkan untuk berbagi data yang lebih kompleks yang tidak dapat Anda ungkapkan sebagai variabel lingkungan (setidaknya tanpa beban berat di pihak Anda), seperti array atau array asosiatif.

Kesalahan fatal
sumber
1
Bagaimana jika saya harus memberikan $ 1 ke sub-shell (karena 'sudo sh -c ...' dipanggil dari skrip)? Haruskah saya mendorong $ 1 ke variabel lingkungan, mengekspornya, dan menggunakan variabel dalam perintah?
Urhixidur
Hanya untuk menambahkan bahwa jika Anda memerlukan hak akses sudo Anda dapat menggunakan sudo -E ... untuk melestarikan variabel lingkungan.
Yucer
2
@FatalError Bisakah Anda menjelaskan sihir di a.sh terakhir, di mana Anda memanggil "./b.sh". Apa arti dari titik pertama? Ini sangat bagus!
Deian
Anda juga dapat mengatur variabel dan nilai ke file dan membaginya seperti itu
newshorts
@Deian, Periode (titik) adalah kependekan dari bash yang dibangun di "source"
Kirill Kost
27

Kesalahan fatal memberikan kemungkinan langsung: sumber skrip kedua Anda! jika Anda khawatir skrip kedua ini dapat mengubah beberapa variabel berharga Anda, Anda selalu dapat sumbernya dalam subkulit:

( . ./test2.sh )

Tanda kurung akan membuat sumber terjadi dalam subkulit, sehingga shell induk tidak akan melihat modifikasi test2.shdapat dilakukan.


Ada kemungkinan lain yang harus dirujuk di sini: gunakan set -a.

Dari referensi POSIXset :

-a: Ketika opsi ini aktif, atribut ekspor harus ditetapkan untuk setiap variabel yang melakukan tugas; lihat volume Definisi Basis IEEE Std 1003.1-2001, Bagian 4.21, Penugasan Variabel . Jika penugasan mendahului nama utilitas dalam suatu perintah, atribut ekspor tidak akan bertahan dalam lingkungan eksekusi saat ini setelah utilitas selesai, dengan pengecualian bahwa sebelum salah satu dari utilitas built-in khusus menyebabkan atribut ekspor tetap ada setelah built-in. di telah selesai. Jika penugasan tidak mendahului nama utilitas dalam perintah, atau jika penugasan adalah hasil dari operasi getopts atau membaca Utilities, atribut ekspor akan bertahan sampai variabel tidak disetel.

Dari Bash Manual :

-a: Tandai variabel dan fungsi yang dimodifikasi atau dibuat untuk ekspor ke lingkungan perintah selanjutnya.

Jadi dalam kasus Anda:

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

Perhatikan bahwa spesifikasi hanya menentukan bahwa dengan set -avariabel ditandai untuk diekspor. Itu adalah:

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

akan bergema cdan bukan baris kosong atau b(yaitu, set +atidak menghapus tanda untuk ekspor, juga tidak "menyimpan" nilai penugasan hanya untuk lingkungan yang diekspor). Ini, tentu saja, perilaku paling alami.

Kesimpulan: menggunakan set -a/ set +abisa kurang membosankan daripada mengekspor secara manual semua variabel. Ini lebih unggul daripada sumber skrip kedua, karena akan bekerja untuk perintah apa pun, tidak hanya yang ditulis dalam bahasa shell yang sama.

gniourf_gniourf
sumber
18

Sebenarnya ada cara yang lebih mudah daripada mengekspor dan tidak memasang atau mencari lagi (setidaknya dalam bash, selama Anda setuju dengan mengirimkan variabel lingkungan secara manual):

biarkan a.sh menjadi

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

dan b.sh be

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

Output yang diamati adalah

[rob @ Archie test] $ ./a.sh
Yo, biar saya beri tahu Anda "Winkle my tinkle", b.sh!
Saya mendengar "winkle my tinkle", yo

Keajaiban terletak pada baris terakhir a.sh, di mana Message, hanya untuk durasi doa ./b.sh, diatur ke nilai secretdari a.sh. Pada dasarnya, ini seperti parameter / argumen bernama. Lebih dari itu, ia bahkan berfungsi untuk variabel seperti $DISPLAY, yang mengontrol X Server tempat aplikasi dimulai.

Ingat, panjang daftar variabel lingkungan tidak terbatas. Pada sistem saya dengan kernel vanilla yang relatif, xargs --show-limitsmemberitahu saya ukuran maksimum dari argumen buffer adalah 2094486 byte. Secara teoritis, Anda menggunakan skrip shell yang salah jika data Anda lebih besar dari itu (pipa, ada orang?)

menyolok
sumber
7

Menambah jawaban Kesalahan Fatal, Ada satu lagi cara untuk meneruskan variabel ke skrip shell lain.

Solusi yang disarankan di atas memiliki beberapa kelemahan:

  1. using Export : Ini akan menyebabkan variabel hadir dari ruang lingkup mereka yang bukan praktik desain yang baik.
  2. using Source : Ini dapat menyebabkan tabrakan nama atau penulisan ulang yang tidak sengaja dari variabel yang telah ditentukan di beberapa file skrip shell lain yang merupakan sumber file lain.

Ada solusi sederhana lain yang tersedia untuk kita gunakan. Mempertimbangkan contoh yang diposting oleh Anda,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

keluaran

hellohelloheloo

Juga penting untuk dicatat bahwa ""perlu jika kita melewati string multi-kata. Ambil satu contoh lagi

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

keluaran

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

Itu terjadi karena alasan yang dijelaskan dalam tautan ini

Subham Tripathi
sumber
6
menggunakan sourcesebenarnya adalah hal yang baik. Mengenai kekhawatiran Anda: menimpa secara tidak sengaja variabel yang telah ditentukan , Anda selalu dapat sumber dalam subkulit. Itu sepenuhnya menyelesaikan masalah.
gniourf_gniourf
@ gniourf_gniourf cara sumber ke subkulit?
Toskan
@Toskan ini disebutkan dalam jawaban saya: ( . ./test2.sh ). Tanda kurung akan membuat Bash menjalankan kontennya dalam sebuah subkulit.
gniourf_gniourf
7

Di Bash jika Anda mengekspor variabel dalam subkulit, menggunakan tanda kurung seperti yang ditunjukkan, Anda menghindari bocornya variabel yang diekspor:

#!/bin/bash

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

Keuntungannya di sini adalah bahwa setelah Anda menjalankan skrip dari baris perintah, Anda tidak akan melihat $ TESTVARIABLE bocor ke lingkungan Anda:

$ ./test.sh
hellohelloheloo
$ echo $TESTVARIABLE
                            #empty! no leak
$
Riaz Rizvi
sumber
Saya menggunakannya tetapi sepertinya tidak berhasil - saya menulis: #! / Bin / bash untuk ((i = 1; i <= 3; i ++)) lakukan (export i source ./script2.sh) lakukan ERROR mengatakan: ./script2.sh:Tidak ada file atau direktori tersebut
Pranjal Gupta
Subkulit sebenarnya tidak diperlukan karena lingkungan tingkat atas ( $prompt perintah) tidak akan pernah melihat yang diekspor $TESTVARIABLE. Ekspor hanya meneruskan salinan variabel ke proses anak berikutnya . Tidak mungkin untuk meneruskan variabel yang mencadangkan rantai ke proses induk kecuali dengan menyimpan nilai ke penyimpanan dan membaca penyimpanan itu nanti dalam skrip proses induk. Perpipaan ke salinan kedua dari skrip induk dimungkinkan tetapi itu bukan proses yang sama . Penjelasan yang bagus dapat ditemukan di sini .
DocSalvager
2

Opsi lain menggunakan eval. Ini hanya cocok jika senar dipercaya. Script pertama dapat mengulangi penugasan variabel:

echo "VAR=myvalue"

Kemudian:

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

Pendekatan ini sangat menarik ketika skrip kedua yang Anda ingin atur variabel lingkungan tidak dalam bash dan Anda juga tidak ingin exportvariabel, mungkin karena mereka sensitif dan Anda tidak ingin mereka bertahan.

Mark Stosberg
sumber
1

Cara lain, yang sedikit lebih mudah bagi saya adalah dengan menggunakan pipa bernama. Named pipes menyediakan cara untuk menyinkronkan dan mengirim pesan di antara berbagai proses.

Memalukan:

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

B. bash:

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

Pemakaian:

$ 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 akan menunggu pesan dan segera setelah A.bash mengirim pesan, B.bash akan melanjutkan pekerjaannya.

Pejman Habashi
sumber