【问题标题】:Pass all variables from one shell script to another?将所有变量从一个 shell 脚本传递到另一个?
【发布时间】:2012-04-04 01:09:51
【问题描述】:

假设我有一个名为 test.sh 的 shell / bash 脚本:

#!/bin/bash

TESTVARIABLE=hellohelloheloo
./test2.sh

我的test2.sh 看起来像这样:

#!/bin/bash

echo ${TESTVARIABLE}

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

有什么不同的方法吗?

【问题讨论】:

  • 一个想法是将变量保存在一个文件中,然后加载到另一个脚本中。
  • @Rodrigo 我之前使用过类似的方法在脚本运行之间“保存”值并取得了一些成功。只需记住一件事,如果脚本的多个实例运行,事情可能会变得很冒险。但是如果你将它们保存为 bash 赋值形式,你可以只获取文件来导入变量

标签: bash shell


【解决方案1】:

另一个选项是使用eval。这仅适用于字符串受信任的情况。第一个脚本可以回显变量赋值:

echo "VAR=myvalue"

然后:

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

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

【讨论】:

    【解决方案2】:

    在 Bash 中,如果您在子 shell 中导出变量,如图所示使用括号,可以避免泄漏导出的变量:

    #!/bin/bash
    
    TESTVARIABLE=hellohelloheloo
    (
    export TESTVARIABLE    
    source ./test2.sh
    )
    

    这里的好处是,从命令行运行脚本后,您不会看到 $TESTVARIABLE 泄漏到您的环境中:

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

    【讨论】:

    • 我使用了它,但它似乎不起作用-我写道:#!/bin/bash for (( i=1;i
    • 实际上不需要子shell,因为*环境(命令行$提示)永远不会看到导出的$TESTVARIABLE。导出只是将变量的副本传递给任何后续子进程。除非将值保存到存储并稍后在父进程脚本中读取该存储,否则不可能将变量沿链传递回父进程。管道到父脚本的第二个副本是可能的,但这不会是相同的进程An excellent explanation can be found in here.
    【解决方案3】:

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

    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 将继续工作。

    【讨论】:

      【解决方案4】:

      致命错误提供了一个简单的可能性:获取您的第二个脚本!如果您担心第二个脚本可能会改变您的一些宝贵变量,您可以随时在子 shell 中获取它:

      ( . ./test2.sh )
      

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


      这里肯定应该引用另一种可能性:使用set -a

      来自POSIX set reference

      -a:当这个选项打开时,export属性应该为每个被赋值的变量设置;请参阅 IEEE Std 1003.1-2001 的基本定义卷,Section 4.21, Variable Assignment。如果命令中的分配位于实用程序名称之前,则 export 属性在实用程序完成后不应保留在当前执行环境中,除非前面的特殊内置实用程序之一导致 export 属性在内置完成后持续存在。如果分配没有在命令中的实用程序名称之前,或者如果分配是 getoptsread 实用程序操作的结果,则导出属性将持续到变量未设置。

      来自Bash Manual

      -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 语言编写的命令。

      【讨论】:

        【解决方案5】:

        实际上有一种比导出和取消设置或再次采购更简单的方法(至少在 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 测试]$ ./a.sh
        哟,让我告诉你“眨眼我的叮当声”,b.sh!
        我听到“眨眼我的叮当声”,哟

        神奇之处在于a.sh 的最后一行,其中Message 仅在./b.sh 调用期间被设置为a.sh 中的secret 的值。 基本上,它有点像命名参数/参数。不仅如此,它甚至还适用于像 $DISPLAY 这样的变量,它控制应用程序在哪个 X 服务器中启动。

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

        【讨论】:

          【解决方案6】:

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

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

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

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

          test.sh

          #!/bin/bash
          
          TESTVARIABLE=hellohelloheloo
          ./test2.sh "$TESTVARIABLE"
          

          test2.sh

          #!/bin/bash
          
          echo $1
          

          输出

          hellohelloheloo
          

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

          ma​​ster.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"
          

          由于this link中恰当描述的原因而发生这种情况

          【讨论】:

          • 使用source其实是件好事。关于您的担心:预定义变量的意外覆盖,您始终可以在子shell 中获取源代码。这完全解决了问题。
          • @gniourf_gniourf 如何获取子shell?
          • @Toskan 我的回答中提到了这一点:( . ./test2.sh )。括号将使 Bash 在子 shell 中运行其内容。
          【解决方案7】:

          你基本上有两种选择:

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

          更新:

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

          A=10
          # ...
          export A
          

          这应该适用于bashshbash 也允许这样组合:

          export A=10
          

          这也适用于 my 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
          

          这些都是 shell 脚本的事实也只是偶然的。环境变量可以传递给您执行的任何进程,例如,如果我们使用 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']
          

          采购:

          相反,我们可以这样获取:

          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 的内容并在相同的shell中执行它。请注意,我们不必导出变量来访问它。这隐式共享您拥有的所有变量,并允许其他脚本在 shell 中添加/删除/修改变量。当然,在这个模型中,你的两个脚本应该是相同的语言(shbash)。举个例子,我们如何来回传递消息:

          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 传递给子 shell 怎么办(因为 'sudo sh -c ...' 是从脚本调用的)?我必须将 $1 塞入环境变量中,然后将其导出并在命令中使用该变量吗?
          • 只是补充一点,如果您需要 sudo 访问权限,您可以使用 sudo -E ... 来保留环境变量。
          • @FatalError 你能解释一下最后一个 a.sh 中的魔法吗,你称之为“../b.sh”。第一个点是什么意思?顺便说一句,效果很好!
          • 您也可以将变量和值设置到文件中并以这种方式共享它们
          • @Deian,句点(点)是“源”中内置 bash 的简写