ส่งผ่านตัวแปรทั้งหมดจากเชลล์สคริปต์หนึ่งไปยังอีกเชลล์หรือไม่


193

ให้บอกว่าฉันมีเชลล์ / ทุบตีสคริปต์ชื่อtest.shด้วย:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

test2.shหน้าตาของฉันเป็นแบบนี้:

#!/bin/bash

echo ${TESTVARIABLE}

สิ่งนี้ใช้ไม่ได้ ฉันไม่ต้องการที่จะผ่านตัวแปรทั้งหมดเป็นพารามิเตอร์ตั้งแต่ imho นี้เป็น overkill

มีวิธีอื่นไหม


แนวคิดหนึ่งคือบันทึกตัวแปรในไฟล์แล้วโหลดในสคริปต์อื่น
Rodrigo

@Rodrigo ฉันเคยใช้วิธีการที่คล้ายกันมาก่อนในการ "บันทึก" ค่าระหว่างรันสคริปต์ด้วยความสำเร็จ สิ่งหนึ่งที่ต้องคำนึงถึงก็คือถ้าหากมีสคริปต์หลายอินสแตนซ์ที่ทำงานสิ่งต่าง ๆ อาจดูแย่ แต่ถ้าคุณบันทึกไว้ในรูปแบบการมอบหมายทุบตีคุณสามารถเพียงแค่แหล่งไฟล์ที่จะนำเข้าตัวแปร
FatalError

คำตอบ:


266

คุณมีสองตัวเลือกโดยทั่วไป:

  1. ทำให้ตัวแปรเป็นตัวแปรสภาพแวดล้อม ( export TESTVARIABLE) ก่อนดำเนินการสคริปต์ที่ 2
  2. แหล่งสคริปต์ที่สองคือ. test2.shและมันจะทำงานในเปลือกเดียวกัน สิ่งนี้จะช่วยให้คุณแบ่งปันตัวแปรที่ซับซ้อนมากขึ้นเช่นอาร์เรย์ได้อย่างง่ายดาย แต่ก็หมายความว่าสคริปต์อื่นสามารถแก้ไขตัวแปรในเชลล์ซอร์สได้

UPDATE:

หากต้องการใช้exportเพื่อตั้งค่าตัวแปรสภาพแวดล้อมคุณสามารถใช้ตัวแปรที่มีอยู่:

A=10
# ...
export A

นี้ควรจะทำงานทั้งในและ bash อนุญาตให้รวมกันได้เช่น:shbash

export A=10

นอกจากนี้ยังใช้งานได้ในของฉัน sh (ซึ่งเกิดขึ้นเป็นbashคุณสามารถใช้echo $SHELLในการตรวจสอบ) แต่ฉันไม่เชื่อว่าจะรับประกันว่าจะทำงานได้ทั้งหมดshดังนั้นควรเล่นให้ปลอดภัยและแยกพวกเขาออก

ตัวแปรที่คุณส่งออกด้วยวิธีนี้จะปรากฏในสคริปต์ที่คุณเรียกใช้ตัวอย่างเช่น:

เถ้า:

#!/bin/sh

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

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

แล้ว:

$ ./a.sh
The message is: hello

ข้อเท็จจริงที่ว่าสิ่งเหล่านี้เป็นเชลล์สคริปต์ทั้งคู่ก็เป็นเรื่องบังเอิญ ตัวแปรสภาพแวดล้อมสามารถส่งผ่านไปยังกระบวนการใด ๆ ที่คุณดำเนินการตัวอย่างเช่นถ้าเราใช้ python แทนมันอาจมีลักษณะดังนี้:

เถ้า:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

จัดหา:

เราสามารถหาแหล่งที่มาเช่นนี้แทน:

เถ้า:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

b.sh:

#!/bin/sh

echo "The message is: $MESSAGE"

แล้ว:

$ ./a.sh
The message is: hello

นี้มากหรือน้อย "นำเข้า" เนื้อหาของb.shโดยตรงและดำเนินการในเปลือกเดียวกัน โปรดสังเกตว่าเราไม่จำเป็นต้องส่งออกตัวแปรเพื่อเข้าถึง สิ่งนี้จะแชร์ตัวแปรทั้งหมดที่คุณมีรวมถึงอนุญาตให้สคริปต์อื่นเพิ่ม / ลบ / แก้ไขตัวแปรในเชลล์ แน่นอนในรุ่นนี้ทั้งสคริปต์ของคุณควรเป็นภาษาเดียวกัน ( shหรือbash) ในการให้ตัวอย่างว่าเราสามารถส่งข้อความกลับไปกลับมาได้อย่างไร:

เถ้า:

#!/bin/sh

MESSAGE="hello"

. ./b.sh

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

b.sh:

#!/bin/sh

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

MESSAGE="goodbye"

แล้ว:

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

มันใช้งานได้ดีอย่างเท่าเทียมกันbashค่ะ นอกจากนี้ยังทำให้ง่ายต่อการแบ่งปันข้อมูลที่ซับซ้อนมากขึ้นซึ่งคุณไม่สามารถแสดงเป็นตัวแปรสภาพแวดล้อมได้ (อย่างน้อยก็ไม่มีการยกของหนัก) เช่นอาร์เรย์หรืออาร์เรย์ที่เชื่อมโยงกัน


1
ถ้าฉันต้องการส่ง $ 1 ไปยัง sub-shell (เพราะ 'sudo sh -c ... ' ถูกเรียกจากสคริปต์) ฉันต้องผลัก $ 1 ลงในตัวแปรสภาพแวดล้อมส่งออกนั้นและใช้ตัวแปรในคำสั่งหรือไม่
Urhixidur

เพียงแค่เพิ่มว่าถ้าคุณต้องการสิทธิ์การเข้าถึง sudo คุณสามารถใช้ sudo -E ... เพื่อรักษาตัวแปรสภาพแวดล้อม
yucer

2
@FatalError คุณช่วยอธิบายความมหัศจรรย์ใน a.sh ล่าสุดที่คุณเรียกว่า "./b.sh" ได้ไหม ความหมายของจุดแรกคืออะไร? มันใช้งานได้ดีมาก!
Deian

คุณสามารถตั้งค่าตัวแปรและค่าให้เป็นไฟล์และแชร์สิ่งเหล่านั้นได้ด้วย
newshorts

@ Deian, ระยะเวลา (จุด) เป็นมือสั้น ๆ สำหรับทุบตีที่สร้างขึ้นใน "แหล่งที่มา"
Kirill Kost

27

ข้อผิดพลาดร้ายแรงมีความเป็นไปได้ตรงไปตรงมา: แหล่งสคริปต์ที่สองของคุณ! หากคุณกังวลว่าสคริปต์ที่สองนี้อาจเปลี่ยนแปลงตัวแปรที่มีค่าของคุณคุณสามารถส่งมาที่ subshell:

( . ./test2.sh )

วงเล็บจะทำให้เกิดแหล่งที่มาใน subshell เพื่อที่เปลือกแม่จะไม่เห็นการแก้ไขtest2.shสามารถทำได้


มีความเป็นไปได้ที่แน่นอนควรจะอ้างอิงที่นี่: set -aการใช้งาน

จากการอ้างอิงPOSIXset :

-a: เมื่อเปิดใช้ตัวเลือกนี้จะมีการตั้งค่าแอตทริบิวต์การส่งออกสำหรับตัวแปรแต่ละตัวที่ดำเนินการมอบหมาย ดูปริมาณความหมายของฐาน IEEE Std 1,003.1-2,001, มาตรา 4.21, การกำหนดตัวแปร หากนำหน้าการกำหนดชื่อสาธารณูปโภคในคำสั่งที่ส่งออกแอตทริบิวต์จะไม่คงอยู่ในสภาพแวดล้อมการดำเนินการในปัจจุบันหลังจากที่เสร็จสมบูรณ์ยูทิลิตี้ที่มีข้อยกเว้นที่หนึ่งก่อน ๆ ของพิเศษในตัวสาธารณูปโภคเป็นสาเหตุที่ทำให้การส่งออกแอตทริบิวต์ยังคงมีอยู่หลังจากที่ภายในกล้อง ในเสร็จเรียบร้อยแล้ว หากการมอบหมายนั้นไม่ได้นำหน้าชื่อยูทิลิตี้ในคำสั่งหรือถ้าการมอบหมายนั้นเป็นผลมาจากการดำเนินการของgetoptsหรืออ่าน ยูทิลิตี้, แอตทริบิวต์การส่งออกจะยังคงอยู่จนกว่าจะมีการตั้งค่าตัวแปร

จากคู่มือ Bash :

-a: ทำเครื่องหมายตัวแปรและฟังก์ชันที่แก้ไขหรือสร้างขึ้นเพื่อส่งออกไปยังสภาพแวดล้อมของคำสั่งที่ตามมา

ดังนั้นในกรณีของคุณ:

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

สังเกตว่ารายละเอียดระบุเพียงว่ามีset -aการทำเครื่องหมายตัวแปรเพื่อการส่งออก นั่นคือ:

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

จะสะท้อนcและไม่ใช่บรรทัดว่างหรือb(นั่นคือset +aไม่ลบเครื่องหมายสำหรับการส่งออกและไม่ "บันทึก" ค่าของการมอบหมายสำหรับสภาพแวดล้อมที่ส่งออกเท่านั้น) แน่นอนว่านี่เป็นพฤติกรรมที่เป็นธรรมชาติที่สุด

สรุป: การใช้set -a/ set +aอาจจะน่าเบื่อน้อยกว่าการส่งออกตัวแปรทั้งหมดด้วยตนเอง มันยอดเยี่ยมกว่าการจัดหาสคริปต์ที่สองเนื่องจากมันจะทำงานสำหรับคำสั่งใด ๆ ไม่เพียง แต่สคริปต์ที่เขียนด้วยภาษาเชลล์เดียวกันเท่านั้น


18

จริงๆแล้วมีวิธีที่ง่ายกว่าการส่งออกและไม่ตั้งค่าหรือการจัดหาอีกครั้ง (อย่างน้อยก็เป็นการทุบตีตราบใดที่คุณตกลงด้วยการส่งตัวแปรสภาพแวดล้อมด้วยตนเอง):

ปล่อยให้เป็น

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

และ b.sh เป็น

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

ผลลัพธ์ที่สังเกตได้คือ

[rob @ Archie test] $ ./a.sh
Yo, lemme บอกคุณ "winkle my tinkle", b.sh!
ฉันได้ยิน "กระพริบตาฉัน"

ความมหัศจรรย์อยู่ในบรรทัดสุดท้ายของการa.shที่Message, เพียงช่วงระยะเวลาของการภาวนาของ./b.shถูกตั้งค่าให้ค่าของจากsecret a.shโดยทั่วไปจะมีพารามิเตอร์ / ข้อโต้แย้งที่กำหนดชื่อเล็กน้อย ยิ่งไปกว่านั้นมันยังใช้งานได้กับตัวแปรเช่น$DISPLAYซึ่งควบคุม X Server ที่แอปพลิเคชันเริ่มทำงาน

โปรดจำไว้ว่าความยาวของรายการตัวแปรสภาพแวดล้อมนั้นไม่มีที่สิ้นสุด ในระบบของฉันที่มีเคอร์เนลวานิลลาค่อนข้างxargs --show-limitsบอกฉันขนาดสูงสุดของบัฟเฟอร์ขัดแย้งคือ 2094486 ไบต์ ในทางทฤษฎีคุณกำลังใช้เชลล์สคริปต์ผิดถ้าข้อมูลของคุณมีขนาดใหญ่กว่านั้น (ท่อทุกคน?)


7

การเพิ่มคำตอบของข้อผิดพลาดร้ายแรงมีอีกวิธีหนึ่งในการส่งตัวแปรไปยังเชลล์สคริปต์อื่น

โซลูชันที่แนะนำข้างต้นมีข้อบกพร่องบางประการ:

  1. using Export : มันจะทำให้ตัวแปรนั้นอยู่นอกขอบเขตซึ่งไม่ใช่วิธีการออกแบบที่ดี
  2. using Source : มันอาจทำให้เกิดการชนกันของชื่อหรือเขียนทับตัวแปรที่กำหนดไว้ล่วงหน้าโดยไม่ตั้งใจในไฟล์เชลล์สคริปต์อื่นซึ่งมีที่มาไฟล์อื่น

มีอีกวิธีง่ายๆที่เราสามารถใช้ได้ พิจารณาตัวอย่างที่คุณโพสต์ไว้

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

เอาท์พุต

hellohelloheloo

นอกจากนี้ยังเป็นสิ่งสำคัญที่จะต้องทราบว่า""มีความจำเป็นหากเราผ่านสตริงหลายคำ ยกตัวอย่างอีกหนึ่งข้อ

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

เอาท์พุต

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

มันเกิดขึ้นเพราะเหตุผลที่อธิบายไว้ในลิงค์นี้


6
การใช้งานsourceจริงเป็นสิ่งที่ดี เกี่ยวกับความกังวลของคุณ: การเขียนทับตัวแปรที่กำหนดไว้ล่วงหน้าโดยไม่ตั้งใจคุณสามารถระบุแหล่งที่มาใน subshell ได้ตลอดเวลา ที่แก้ปัญหาได้อย่างสมบูรณ์
gniourf_gniourf

@gniourf_gniourf วิธีการที่มาลงใน subshell?
Toskan

@Toskan สิ่งนี้ถูกกล่าวถึงในคำตอบของฉัน: ( . ./test2.sh ). วงเล็บจะทำให้ Bash เรียกใช้เนื้อหาในเชลล์ย่อย
gniourf_gniourf

7

ใน Bash หากคุณส่งออกตัวแปรภายใน subshell โดยใช้วงเล็บดังที่แสดงไว้คุณหลีกเลี่ยงการรั่วไหลของตัวแปรที่ส่งออก:

#!/bin/bash

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

ข้อดีที่นี่คือหลังจากที่คุณเรียกใช้สคริปต์จากบรรทัดคำสั่งคุณจะไม่เห็น $ TESTVARIABLE ที่รั่วไหลออกมาในสภาพแวดล้อมของคุณ:

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

ฉันใช้มัน แต่ดูเหมือนว่าจะใช้งานไม่ได้ - ฉันเขียน: #! / bin / bash สำหรับ ((i = 1; i <= 3; i ++)) ทำ (ส่งออก i source ./script2.sh) ทำผิดพลาด พูดว่า: ./script2.sh: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว
Pranjal Gupta

subshell ไม่จำเป็นต้องใช้จริงตั้งแต่ระดับบนสภาพแวดล้อม (คน commandline $พรอมต์) $TESTVARIABLEจะไม่เคยเห็นที่ส่งออก การส่งออกเพียงส่งสำเนาของตัวแปรไปยังกระบวนการลูกที่ตามมา เป็นไปไม่ได้ที่จะส่งตัวแปรสำรองเชนไปยังกระบวนการพาเรนต์ยกเว้นการบันทึกค่าไปยังหน่วยเก็บข้อมูลและอ่านหน่วยเก็บข้อมูลนั้นในสคริปต์กระบวนการพาเรนต์ ท่อกับสำเนาที่สองของสคริปต์ผู้ปกครองเป็นไปได้ แต่ที่จะไม่เหมือนกันกระบวนการ คำอธิบายที่ดีสามารถพบได้ในที่นี่
DocSalvager

2

evalอีกตัวเลือกหนึ่งคือการใช้ สิ่งนี้จะเหมาะสมก็ต่อเมื่อสตริงนั้นเชื่อถือได้ สคริปต์แรกสามารถสะท้อนการกำหนดตัวแปร:

echo "VAR=myvalue"

แล้ว:

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

วิธีการนี้มีความสนใจเป็นพิเศษเมื่อสคริปต์ตัวที่สองที่คุณต้องการตั้งค่าตัวแปรสภาพแวดล้อมไม่ได้อยู่ใน bash และคุณไม่ต้องการexportตัวแปรเนื่องจากอาจมีความอ่อนไหวและคุณไม่ต้องการให้มันคงอยู่


1

อีกวิธีซึ่งง่ายกว่าสำหรับฉันเล็กน้อยคือใช้ท่อที่มีชื่อ ไปป์ที่มีชื่อให้วิธีการซิงโครไนซ์และส่งข้อความระหว่างกระบวนการต่าง ๆ

A.bash:

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

B.bash:

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

การใช้งาน:

$ 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 จะรอข้อความและทันทีที่ A.bash ส่งข้อความ B.bash จะทำงานต่อไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.