将所有变量从一个Shell脚本传递到另一个?


192

可以说我有一个名为/的shell / bash脚本test.sh

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

我的test2.sh样子是这样的:

#!/bin/bash

echo ${TESTVARIABLE}

这是行不通的。我不想将所有变量都作为参数传递,因为恕我直言,这太过分了。

有其他方法吗?


一个想法是将变量保存在文件中,然后加载到另一个脚本中。
罗德里戈(Rodrigo)

@Rodrigo我之前使用过类似的方法来成功保存脚本运行之间的值。要记住的一件事是,如果脚本的多个实例运行,那么事情可能会变得扑朔迷离。但是,如果将它们保存为bash分配形式,则只需提供源文件即可导入变量
FatalError 2012年

Answers:


264

您基本上有两种选择:

  1. export TESTVARIABLE在执行第二个脚本之前,将变量设置为环境变量()。
  2. 源第二个脚本,即. test2.sh它将在同一外壳中运行。这将使您轻松共享数组等更复杂的变量,但也意味着其他脚本可以在源shell中修改变量。

更新:

要用于export设置环境变量,可以使用现有变量:

A=10
# ...
export A

这应该同时适用于bashshbash还允许将其合并,如下所示:

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

这些都是shell脚本的事实也是偶然的。可以将环境变量传递给您执行的任何进程,例如,如果我们改用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直接“导入”了内容并在同一shell中执行。注意,我们不必导出变量即可访问它。这隐式共享您拥有的所有变量,并允许其他脚本在外壳中添加/删除/修改变量。当然,在此模型中,两个脚本都应使用相同的语言(shbash)。举个例子,我们如何来回传递消息:

灰:

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

只需添加一下,如果您需要sudo访问权限,则可以使用sudo -E ...保留环境变量。
yucer '16

2
@FatalError您能否在最后一个a.sh(您称其为“ ../b.sh”)中解释其神奇之处。第一个圆点是什么意思?顺便说一句!
Deian

您还可以将变量和值设置到文件中并以这种方式共享它们
newshorts,

@Deian,句号(“点”)是“源”中建立的bash的简写形式
Kirill Kost

27

致命错误提供了一种直接的可能性:编写第二个脚本!如果您担心第二个脚本可能会更改一些宝贵的变量,则可以始终在子shell中提供它:

( . ./test2.sh )

括号将使源代码出现在子shell中,因此父shell将看不到修改test2.sh可以执行的情况。


这里肯定还有另外一种可能需要引用:use set -a

POSIX set参考

-a:启用此选项时,应为每个要执行赋值的变量设置导出属性;请参阅《 IEEE标准1003.1-2001的基本定义》第4.21节“变量赋值”。如果分配在命令中位于实用程序名称之前,则该实用程序完成后,export属性不应在当前执行环境中保留,但特殊的内置实用程序之一的前面会导致export属性在内置的实用程序之后持续存在。中已完成。如果该赋值不在命令中的实用程序名称之前,或者该赋值是getoptsread操作的结果 实用程序中,导出属性将一直存在,直到未设置变量为止。

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可以比手动导出所有变量少乏味。它比采购第二个脚本优越,因为它适用于任何命令,不仅适用于使用相同Shell语言编写的命令。


18

实际上,有比重新导出和取消设置或重新寻找资源更简单的方法(至少在bash中,只要您可以手动传递环境变量即可):

让a.sh为

#!/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
哟,lemme告诉你“眨眼我的叮当声”,b.sh!
我听到“我的叮当声”,哟

神奇之处在于的最后一行a.sh,其中Message仅在调用的持续时间内./b.sh,将其设置为secretfrom 的值a.sh。基本上,它有点像命名参数/参数。不仅如此,它甚至还适用于变量,例如$DISPLAY,它控制应用程序从哪个X Server启动。

请记住,环境变量列表的长度不是无限的。在具有相对xargs --show-limits原始内核的系统上,告诉我参数缓冲区的最大大小为2094486字节。从理论上讲,如果您的数据大于该范围(管道,任何人?),则您使用的Shell脚本是错误的


7

除了致命错误的答案之外,还有另一种将变量传递给另一个Shell脚本的方法。

上述建议的解决方案有一些缺点:

  1. using Export :这将导致变量超出其范围,这不是一个好的设计实践。
  2. using Source :这可能会导致名称冲突或意外地覆盖源于另一个文件的某些其他Shell脚本文件中的预定义变量。

有另一个简单的解决方案可供我们使用。考虑到您发布的示例,

test.sh

#!/bin/bash

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

test2.sh

#!/bin/bash

echo $1

输出

hellohelloheloo

同样重要的是要注意,""如果我们传递多字字符串,这是必需的。再举一个例子

大师

#!/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实际上是一件好事。关于您的担心:意外覆盖预定义变量,您始终可以在subshel​​l中进行获取。那完全解决了问题。
gniourf_gniourf 2015年

@gniourf_gniourf如何导入到subshel​​l?
Toskan 2015年

@Toskan我的答案中提到了( . ./test2.sh )。括号将使Bash在子shell中运行其内容。
gniourf_gniourf 2015年

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 ++))做(导出i源./script2.sh)做错了说:
./script2.sh

由于顶层环境(命令行$提示符)将永远不会看到导出的,因此实际上不需要该子Shell $TESTVARIABLE。导出只是将变量的副本传递给任何后续的子进程。除非将值保存到存储器中,然后再在父过程脚本中读取该存储器,否则不可能将变量传递回链到父过程。可以使用父脚本的第二个副本,但这不是同一过程在这里可以找到很好的解释
DocSalvager

2

另一种选择是使用eval。仅当字符串受信任时才适用。第一个脚本可以回显变量分配:

echo "VAR=myvalue"

然后:

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

当您要为其设置环境变量的第二个脚本不在 bash中并且您也不想使用export该变量时,这种方法特别有用,这可能是因为它们是敏感的并且您不希望它们持久化。


1

另一种方法(对我来说更容易一点)是使用命名管道。命名管道提供了一种在不同进程之间同步和发送消息的方法。

惑:

#!/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将继续其工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.