【问题标题】:Sharing variables between two independent shell scripts在两个独立的 shell 脚本之间共享变量
【发布时间】:2016-04-04 03:51:40
【问题描述】:

我正在尝试从一个脚本中导出一个变量并在另一个脚本中使用它,但是这个变量没有被导出,这可能是什么问题

将此视为脚本1

#!/bin/sh
export x=19
exit 0

将此视为脚本2

#!/bin/sh
echo "x="${x}
exit 0

我将它们作为两个独立的脚本执行,例如

desktop:~/Desktop/Trail_Programs$ sh script1.sh
desktop:~/Desktop/Trail_Programs$ sh script2.sh

输出是

desktop:~/Desktop/Trail_Programs$ sh script2.sh 
x=

【问题讨论】:

标签: bash shell


【解决方案1】:

sourcescript1.sh 来自script2.sh

script2.sh的开头(shebang之后),添加:

source /path/to/script1.sh

【讨论】:

  • 虽然这在某些情况下可能会奏效,但对于并非设计为来源的脚本而言,通常会出现问题。除了可能污染调用 shell 的环境之外,更大的问题是源脚本中的 exit 语句(如 OP 的示例脚本所示),因为它们也不可避免地会立即退出调用 shell。
  • @mklement0 duh..missed exit..无论如何,在大多数类似情况下,主要直觉是source第二个脚本..
【解决方案2】:

请记住,脚本可能绝不影响其父级的环境。 从不表示从不。所以遵守规则:

#!/bin/bash
export x=19
exit 0

对环境有NO影响,不在那里设置x=anythingexport 只会影响从该脚本中创建的 subshel​​ls 的环境。例如拿你的脚本,叫它exp.sh:

#!/bin/bash
export x=19
./script2.sh
exit 0

script2.sh 你有:

echo "x = $x"

那么,也只有这样,你会得到:

$ bash exp.sh
x = 19

这只是规则......

【讨论】:

  • ++;但是./script2.sh 不会创建子shell,它会创建一个恰好是shell 的子进程。导出的变量成为对任何子进程可见的环境变量,无论它是否是shell;另外,我建议说“不能影响其父母的环境”。
  • 假设我必须独立执行这两个脚本,两个脚本之间没有父子关系,我该怎么办??
  • 你违反了规则。当您需要在环境中设置某些内容时。您仅限于/etc/profile/etc/bashrc$HOME 中的版本(例如~/.bashrc)。这就是 Linux shell 的工作方式。您可以将包含变量的 source 代码放入当前的 shell/脚本,但不能影响父级。想想PS1PATH 的设置位置和方式。这就是原因。你总是可以写一个tmp 文件并从script2 读取它,你只是不能使用环境。
  • @mklement0 一如既往,你在措辞上是正确的,我本可以更好地措辞......
  • @DavidC.Rankin 写入 tmp 文件 file 创建一个 tmp~ 文件, script1 从 tmp~ 文件读取数据
【解决方案3】:

David C. Rankin's helpful answer 解释了为什么您的代码不起作用。

注意:
仅当无法修改您使用的脚本时才应使用以下解决方案(如果您想了解采购和临时环境变量,该解决方案也可能很有趣)。
否则,请参阅底部讨论的解决方案。

要解决您的问题,您可以尝试以下方法:

x=$(trap 'printf %s "$x"' EXIT; . script1.sh >/dev/null) sh script2.sh

但请注意:

  • script1.sh 将 - 必然 - 由你的 current shell 执行,它可能是也可能不是 sh
  • script1.sh 的 stdout 输出被抑制,因为必须确保 printf %s "$x" 是命令替换 ($(...)) 内的子 shell 生成的唯一 stdout 输出。
  • 这种方法仅限于单个变量(尽管它可以扩展为输出多个变量的值,然后调用脚本必须将其解析回单个值)。

. script1.sh sources 子shell里面的脚本,这意味着子shell直接在自己的环境中执行script1.sh,因此在执行之后会看到脚本的变量(这意味着它会看到@ 987654331@ 即使它没有导出)。

trap 'printf %s "$x"' EXIT设置出口陷阱;即子shell退出时执行的代码,在本例中为printf %s "$x",它只是输出感兴趣的变量的值。
请注意,即使script1.shexit 语句而终止,这种方法对于确保打印$x 的值是必要的;由于script1.sh 由子shell获取exit 退出整个子shell。

x=$(...) 捕获该值,并通过 预先添加 到命令 sh script2.sh,有效地使生成的 $x 成为 script2.sh 然后的 环境 变量看到了。


通常获取并非设计为可获取的脚本是有问题的:

  • 所有由脚本修改或创建的变量以及脚本对 shell 环境所做的任何更改都会影响调用 shell。
  • 如果此类脚本执行exit,它们也会退出调用shell。

如果涉及的脚本由于某种原因无法修改,上面的解决方案是最好的选择,因为它绕过了这些问题,尽管有一些限制。


更强大的通用解决方案(需要修改脚本):

让设置感兴趣的环境变量的脚本自身调用另一个脚本是正确的解决方案,如David's answer 所示。

如果脚本确实需要作为 peers 运行,则需要通过 files 传递值:让设置感兴趣变量的脚本将它们的值写入一个(临时)文件并让其他脚本读取该文件:

script1.sh:

#!/bin/sh
export x=19
# Write to temp. file named for the *parent* process ID.
# Since both script calls will have the same parent process, this 
# allows you to avoid a static filename subject to name collisions.
printf %s "$x" > /tmp/x.$PPID

script2.sh:

#!/bin/sh
# Read the value of $x from the temp. file, then delete the file.
x=$(cat /tmp/x.$PPID) && rm /tmp/x.$PPID
echo "x=${x}"

【讨论】:

    【解决方案4】:

    假设你有文件tobeincluded1,内容如下

    #this file tobeincluded1 will be included in master
    x=16
    

    和文件tobeincluded2 包含以下内容

    #this file tobeincluded2 will be included in master
    y=21
    

    您可以使用.source 将上述文件包含在您的脚本中,如下所示:

    #!/bin/bash
    
    . ./tobeincluded1   #using the . to include a file
    source ./tobeincluded2 #using the source command to include a file
    
    echo "x : $x"
    echo "y : $y"
    

    【讨论】: