Передати всі змінні з одного сценарію оболонки в інший?


193

Скажімо, у мене є сценарій оболонки / bash з ім'ям test.sh:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

Моє test2.shвиглядає так:

#!/bin/bash

echo ${TESTVARIABLE}

Це не працює. Я не хочу передавати всі змінні як параметри, оскільки imho це зайвий.

Чи є інший спосіб?


Одна ідея - зберегти змінні у файлі, а потім завантажити в інший сценарій.
Родріго

@Rodrigo Я раніше використовував подібний підхід для "збереження" значень між запусками сценарію з деяким успіхом. Тільки одне, що потрібно пам’ятати, - це те, що якщо запущено кілька екземплярів сценаріїв, речі можуть стати важкими. Але якщо ви збережете їх у формі призначення bash, ви можете просто надати файл для імпорту змінних
FatalError

Відповіді:


266

У вас є два варіанти:

  1. Зробіть змінну середовищем змінною ( export TESTVARIABLE) перед виконанням 2-го сценарію.
  2. Джерело другого сценарію, тобто . test2.shвін буде працювати в одній оболонці. Це дозволить вам легко ділитися складнішими змінними, такими як масиви, але також означає, що інший скрипт міг би змінювати змінні у вихідній оболонці.

ОНОВЛЕННЯ:

Щоб exportвстановити змінну середовища, ви можете використовувати наявну змінну:

A=10
# ...
export A

Це повинно працювати в обох bashі sh. bashтакож дозволяє поєднувати його так:

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']

Sourcing:

Натомість ми можемо джерелом так:

зола:

#!/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 на підкожу (тому що "sudo sh -c ..." викликається зі сценарію)? Повинен я занести $ 1 до змінної середовища, експортувати її та використовувати змінну в команді?
Урхіксідур

Просто додамо, що якщо вам потрібні права доступу до sudo, ви можете використовувати sudo -E ... для збереження змінних середовища.
Юсер

2
@FatalError Чи можете ви, будь ласка, пояснити магію в останньому a.sh, куди ви дзвоните "./b.sh". Яке значення має перша крапка? Це чудово працює до речі!
Діян

Ви також можете встановити змінні та значення у файл та поділитися ними таким чином
новинних коротів,

@Deian, Період (крапка) - це коротка рука для вбудованого "джерела"
Кирило Кост

27

Фатальна помилка дала прямо можливість: джерело другого сценарію! якщо ви переживаєте, що цей другий сценарій може змінити деякі ваші дорогоцінні змінні, ви завжди можете його джерело в нижній частині:

( . ./test2.sh )

Круглі дужки змусять джерело траплятися в підпакеті, так що батьківська оболонка не буде бачити модифікацій, які test2.shможуть виконувати.


Тут є інша можливість, на яку слід обов'язково згадати: використання set -a.

З посилання POSIXset :

-a: Коли цей параметр увімкнено, для кожної змінної, якій виконується призначення , встановлюється атрибут експорту ; див. Обсяг базових визначень IEEE Std 1003.1-2001, Розділ 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

Насправді є простіший спосіб, ніж експортувати та скасовувати або знову шукати джерело (принаймні, в bash, якщо ви все в порядку з передачею змінних середовища вручну):

нехай a.sh буде

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

і б.ш бути

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

Спостерігається вихід

[пограбуй @ тест Арчі] $ ./a.sh
Йо, лемме скажу тобі "підморгни моє тинько", б.ш!
Я почув, як "моргає моє тинько", йо

Магія полягає в останньому рядку a.sh, де Messageлише протягом тривалості виклику ./b.shвстановлено значення secretз a.sh. В основному, це трохи нагадує названі параметри / аргументи. Більше того, він навіть працює для подібних змінних $DISPLAY, які керують, на якому сервері X Server запускається програма.

Пам’ятайте, довжина списку змінних довкілля не є нескінченною. У моїй системі з відносно ванільним ядром xargs --show-limitsповідомляє мені, що максимальний розмір буфера аргументів становить 2094486 байт. Теоретично ви неправильно використовуєте сценарії оболонки, якщо ваші дані є більшими за це (труби, хтось?)


7

Додаючи до відповіді Fatal Error, Є ще один спосіб передати змінні в інший скрипт оболонки.

У запропонованому вище рішенні є деякі недоліки:

  1. using Export : Це призведе до того, що змінна буде представлена ​​поза їх рамками, що не є хорошою практикою проектування.
  2. using Source : Це може спричинити зіткнення імен або випадкове перезапис заздалегідь заданої змінної в якомусь іншому файлі сценарію оболонки, який створив інший файл.

Є ще одне просте рішення, доступне для нас. Розглядаючи приклад, опублікований вами,

test.sh

#!/bin/bash

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

тест2.ш

#!/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

раб2.ш

#!/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насправді хороша річ. Що стосується ваших турбот: випадкове перезапис заздалегідь заданої змінної , ви завжди можете надсилати джерело в підрозділ. Це повністю вирішує проблему.
gniourf_gniourf

@gniourf_gniourf як джерело в нижню частину?
Тоскан

@Toskan Про це йдеться в моїй обороні: ( . ./test2.sh ). Круглі дужки змусять Bash запускати його вміст у підрозділ.
gniourf_gniourf

7

Якщо Bash експортує змінну в підзаголовку, використовуючи круглі дужки, як показано, ви уникаєте протікання експортованих змінних:

#!/bin/bash

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

Перевага тут полягає в тому, що після запуску сценарію з командного рядка ви не побачите, що $ TESTVARIABLE просочився у ваше середовище:

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

Я використовував це, але це, здається, не працює - я написав: #! / Bin / bash for ((i = 1; i <= 3; i ++)) do (export i source ./script2.sh) зробив ПОМИЛКУ говорить: ./script2.sh: Немає такого файлу чи каталогу
Pranjal Gupta

Підзарядка фактично не потрібна, оскільки середовище верхнього рівня (командний $рядок) ніколи не побачить експортований $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

Б.баш:

#!/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 продовжить свою роботу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.