Передать все переменные из одного скрипта в другой?

194

Допустим, у меня есть сценарий shell / bash test.shс именем :

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

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

#!/bin/bash

echo ${TESTVARIABLE}

Это не работает. Я не хочу передавать все переменные в качестве параметров, так как имхо это излишне.

Есть ли другой способ?

Toskan
источник
Одна идея - сохранить переменные в файле, а затем загрузить в другой скрипт.
Родриго
@Rodrigo Ранее я использовал похожий подход для «сохранения» значений между запусками скрипта с некоторым успехом. Следует помнить только одно: если запускается несколько экземпляров сценариев, все может стать рискованным. Но если вы сохраните их в форме назначения bash, вы можете просто получить исходный файл для импорта переменных
FatalError

Ответы:

267

У вас есть в основном два варианта:

  1. Сделайте переменную переменной среды ( export TESTVARIABLE) перед выполнением 2-го сценария.
  2. Исходный 2-й скрипт, т.е. . test2.shи он будет работать в той же оболочке. Это позволит вам легко обмениваться более сложными переменными, такими как массивы, но также означает, что другой скрипт может изменять переменные в исходной оболочке.

ОБНОВИТЬ:

Чтобы использовать exportдля установки переменной среды, вы можете использовать существующую переменную:

A=10
# ...
export A

Это должно работать в обоих bashи sh. bashтакже позволяет комбинировать его так:

export A=10

Это также работает в моем sh (что может быть bash, вы можете использовать echo $SHELLдля проверки). Но я не верю, что это гарантированно сработает во всем sh, поэтому лучше оберегать их и разделять их.

Любая переменная, которую вы экспортируете таким образом, будет видна в выполняемых вами скриптах, например:

a.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, он может выглядеть так:

a.sh:

#!/bin/sh

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

b.py:

#!/usr/bin/python

import os

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

Sourcing:

Вместо этого мы могли бы получить такой источник:

a.sh:

#!/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). Чтобы привести пример, как мы могли бы передавать сообщения туда и обратно:

a.sh:

#!/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 в переменную окружения, экспортировать ее и использовать переменную в команде?
Urhixidur
Просто добавьте, что если вам нужны права доступа sudo, вы можете использовать sudo -E ... для сохранения переменных окружения.
yucer
2
@FatalError Можете ли вы объяснить магию в последнем a.sh, где вы называете «./b.sh». Что означает первая точка? Это прекрасно работает, кстати!
Deian
Вы также можете установить переменные и значения в файл и обмениваться ими таким образом
newshorts
@Deian, точка (точка) - короткая рука для bash, встроенного в «источник»
Кирилл Кост
27

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

( . ./test2.sh )

Скобки заставят источник происходить в подоболочке, так что родительская оболочка не увидит, какие изменения test2.shмогут быть выполнены.


Есть еще одна возможность, на которую обязательно нужно ссылаться: использовать set -a.

Из ссылки на POSIXset :

-a: Когда эта опция включена, атрибут экспорта должен быть установлен для каждой переменной, которой выполняется присвоение; см. том Базовых определений IEEE Std 1003.1-2001, Раздел 4.21, Присвоение переменных . Если назначение предшествует имени утилиты в команде, атрибут экспорта не должен сохраняться в текущей среде выполнения после завершения утилиты, за исключением того, что перед одной из специальных встроенных утилит атрибут экспорта сохраняется после встроенной в завершено. Если назначение не предшествует имени утилиты в команде, или если назначение является результатом операции getopts или read утилит, атрибут экспорта должен сохраняться до тех пор, пока переменная не будет сброшена.

Из руководства 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может быть менее утомительным, чем экспорт всех переменных вручную. Это лучше, чем поиск второго сценария, так как он будет работать для любой команды, а не только для тех, которые написаны на одном языке оболочки.

gniourf_gniourf
источник
18

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

пусть будет

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

Наблюдаемый вывод

[rob @ Archie test] $ ./a.sh
Эй, позволь сказать тебе "подмигни мне, дурак", b.sh!
Я слышал, "подмигните мне звон", эй

Магия находится в последней строке a.sh, где Messageтолько для продолжительности вызова ./b.shустановлено значение secretfrom a.sh. По сути, это немного похоже на именованные параметры / аргументы. Более того, он даже работает для таких переменных, как $DISPLAY, например , которые контролируют, на каком X сервере запускается приложение.

Помните, что длина списка переменных среды не бесконечна. В моей системе с относительно ванильным ядром, xargs --show-limitsговорит мне, максимальный размер буфера аргументов составляет 2094486 байт. Теоретически, вы используете неправильные сценарии оболочки, если ваши данные больше, чем эти (каналы, кто-нибудь?)

поразительный
источник
7

Добавление к ответу Fatal Error, есть еще один способ передать переменные в другой скрипт оболочки.

Выше предложенное решение имеет некоторые недостатки:

  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на самом деле хорошая вещь. Что касается вашего беспокойства: случайная перезапись предопределенной переменной , вы всегда можете получить источник в подоболочке. Это полностью решает проблему.
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 (экспорт i source ./script2.sh) сделал ERROR это говорит: ./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

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 продолжит свою работу.

Пейман Хабаши
источник