【问题标题】:Unix: What is the difference between source and export?Unix:源和导出有什么区别?
【发布时间】:2013-03-06 15:52:03
【问题描述】:

我正在编写一个 shell 脚本,以读取具有 key=value 对的文件并将这些变量设置为环境变量。但我有一个疑问,如果我这样做 source file.txt 是否会将在该文件中定义的变量设置为环境变量,或者我应该逐行读取文件并使用导出命令进行设置?

在这种情况下,源命令与导出命令不同吗?

【问题讨论】:

  • 你用的是什么外壳?
  • 你为什么不试着找出答案?
  • 我会非常警惕source-ing 在生产环境中的 shell 脚本中使用用户定义的文件。想象一下一个心怀不满的员工添加了rm -rf ${HOME}(或更糟)这一行......
  • 相关,非常有帮助,更详细:askubuntu.com/questions/862236/…

标签: linux unix


【解决方案1】:

当您source 文件时,将设置分配但不会导出变量,除非设置了allexport 选项。如果要导出所有变量,使用allexportsource 文件比读取文件并显式使用export 要简单得多。换句话说,你应该这样做:

set -a
. file.txt

(我更喜欢.,因为它比source 更便携,但sourcebash 中工作得很好。)

请注意,导出变量不会使其成为环境变量。它只是使它成为任何子shell中的环境变量。

【讨论】:

  • Note that exporting a variable does not make it an environment variable. It just makes it an environment variable in any subshell. 所以 1) 什么是环境变量? 2)如何将变量设为环境变量?
  • 在大多数情况下,变量是否在环境中并不重要。环境变量将被所有子进程继承,已导出的非环境变量也是如此。至于“什么是环境变量?”……它是环境中的变量!出于 shell 的实际目的,这仅意味着它是一个在调用 shell 时从其父级继承的变量。一些 shell 家族提供了显式的 setenv 函数来将变量放入环境中,但并非全部都这样做。
  • 感谢您的回复。请注意,我添加了一个答案,并提供了exportsource (.) 的附加解释和示例,我认为它们非常有用,在这里:stackoverflow.com/a/62626515/4561887。在一段时间没有编写 bash 脚本之后,我一直忘记这些事情的含义,所以我将我的答案称为“规范”,这意味着我计划每次忘记一个细节或一次又一次地回到自己的答案需要复习一下。
【解决方案2】:

source (.) vs export(最后还有一些文件锁 [flock] 的东西):

简而言之:

  1. source some_script.sh,或与 POSIX 兼容的等效项 . some_script.sh从其他脚本中引入变量,而
  2. export my_var="something" 将变量推送到从当前脚本/进程调用/启动的其他脚本/进程

在 Linux shell 脚本中使用 source some_script.sh. some_script.sh 有点像在 Python 中使用 import some_module 或在 C 或 C++ 中使用 #include <some_header_file.h>它会从源代码中引入变量。

使用export some_var="something" 有点像在本地设置该变量,因此它可用于当前脚本或进程的其余部分,然后将其传递给任何和所有子脚本或从此时起您可能会调用的进程。

更多细节:

所以,这个:

# export `some_var` so that it is set and available in the current script/process,
# as well as in all sub-scripts or processes which are called from the
# current script/process
export some_var="something"
# call other scripts/processes, passing in `some_var` to them automatically
# since it was just exported above! 
script1.sh  # this script now gets direct access to `some_var`
script2.sh  # as does this one
script3.sh  # and this one

好像你已经这样做了:

# set this variable for the current script/process only
some_var="something" 
# call other scripts/processes, passing in `some_var` to them **manually**
# so they can use it too 
some_var="something" script1.sh  # manually pass in `some_var` to this script
some_var="something" script2.sh  # manually pass in `some_var` to this script
some_var="something" script3.sh  # manually pass in `some_var` to this script

除了上面的第一个版本,我们调用 export some_var="something" 实际上具有将变量递归传递或导出到子进程,所以如果我们从当前脚本/进程内部调用 script1.sh,那么 script1.sh 将从我们当前的脚本中获取导出的变量,如果script1.sh调用script5.sh,并且script5.sh调用script10.sh,那么这两个脚本也会自动获取导出的变量。这与上面的手动情况相反,只有那些在调用脚本时使用手动设置变量显式调用的脚本才能获取它们,因此子脚本不会自动从它们的调用脚本中获取任何变量!

如何“取消导出”变量:

请注意,一旦您导出了一个变量,对其调用 unset 将“取消导出”,如下所示:

# set and export `some_var` so that sub-processes will receive it
export some_var="something"
script1.sh  # this script automatically receives `some_var`

# unset and un-export `some_var` so that sub-processes will no longer receive it
unset some_var
script1.sh  # this script does NOT automatically receive `some_var`

总结:

  1. source. 导入
  2. export 出口
  3. unset 不出口

示例:

创建这个脚本:

source_and_export.sh

#!/bin/bash

echo "var1 = $var1"
var2="world"

然后将其标记为可执行:

chmod +x source_and_export.sh

现在我在终端上运行一些命令来测试source (.) 和export 使用此脚本的命令。在以$ 开头的行之后输入您看到的命令(不包括 cmets)。其他行是输出。按顺序运行命令,一次一个命令:

$ echo "$var1"              # var1 contains nothing locally

$ var1="hello"              # set var1 to something in the current process only
$ ./source_and_export.sh    # call a sub-process
var1 =                      # the sub-process can't see what I just set var1 to
$ export var1               # **export** var1 so sub-processes will receive it
$ ./source_and_export.sh    # call a sub-process
var1 = hello                # now the sub-process sees what I previously set var1 to
$ echo "$var1 $var2"        # but I can't see var2 from the subprocess/subscript
hello 
$ . ./source_and_export.sh  # **source** the sub-script to _import_ its var2 into the current process
var1 = hello
$ echo "$var1 $var2"        # now I CAN see what the subprocess set var2 to because I **sourced it!**
hello world                 # BOTH var1 from the current process and var2 from the sub-process print in the current process!
$ unset var1                # unexport (`unset`) var1
$ echo "$var1"              # var1 is now NOT set in the current process
$ ./source_and_export.sh    # and the sub-process doesn't receive it either
var1 = 
$ var1="hey"                # set var1 again in the current process
$ . ./source_and_export.sh  # if I **source** the script, it runs in the current process, so it CAN see var1 from the current process!
var1 = hey                  # notice it prints
$ ./source_and_export.sh    # but if I run the script as a sub-process, it can NOT see var1 now because it was `unset` (unexported)
var1 =                      #   above and has NOT been `export`ed again since then!
$

在进程之间使用文件作为全局变量

有时,特别是在编写脚本来启动程序和事物时,我会遇到export 似乎无法正常工作的情况。在这些情况下,有时必须求助于使用文件本身作为全局变量来将信息从一个程序传递到另一个程序。这是如何做到的。在这个例子中,文件“~/temp/.do_something”的存在作为一个进程间布尔变量:

# In program A, if the file "~/temp/.do_something" does NOT exist, 
# then create it
mkdir -p ~/temp
if [ ! -f ~/temp/.do_something ]; then
    touch ~/temp/.do_something  # create the file
fi


# In program B, check to see if the file exists, and act accordingly

mkdir -p ~/temp
DO_SOMETHING="false"
if [ -f ~/temp/.do_something ]; then
    DO_SOMETHING="true"
fi

if [ "$DO_SOMETHING" == "true" ] && [ "$SOME_OTHER_VAR" == "whatever" ]; then 
    # remove this global file "variable" so we don't act on it again
    # until "program A" is called again and re-creates the file
    rm ~/temp/.do_something 
    do_something
else
    do_something_else
fi

如上所示,仅检查文件的存在,非常适合在程序和进程之间全局传递布尔条件。但是,如果您需要传递更复杂的变量,例如字符串或数字,您可能需要将这些值写入文件。在这种情况下,您应该使用 文件锁定 功能flock,以正确确保进程间同步。它是一种进程安全(即:“进程间”)互斥体原语。你可以在这里阅读:

  1. shell 脚本flock 命令:https://man7.org/linux/man-pages/man1/flock.1.html。另请参阅 man flockman 1 flock
  2. Linux 库 C 命令:https://man7.org/linux/man-pages/man2/flock.2.html。另见man 2 flock。您必须在 C 文件中 #include <sys/file.h> 才能使用此功能。

参考资料:

  1. https://askubuntu.com/questions/862236/source-vs-export-vs-export-ld-library-path/862256#862256
  2. 我自己的实验和测试
  3. 我将把上面的例子添加到我在 GitHub 上的项目中,在 bash 文件夹下:https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world

【讨论】:

  • 很棒的解释! import#include 以及“将其传递给您从此时起可能调用的任何和所有子脚本或进程”确实有助于澄清这个概念!
  • 感谢您的来信!很高兴你发现它有用。我也一直忘记这些东西,并且真的回到这里并定期参考我自己的答案。我把事情写下来,以便稍后再回来。
  • 我又来了……阅读我自己的答案。我对export 再次做了什么以及何时必须使用它以及如何将变量传递到您正在调用的脚本中感到困惑——我刚刚在我的回答中看到了答案!例如:some_var="something" script1.sh # manually pass in 'some_var' to this script.
猜你喜欢
  • 2011-03-19
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
  • 2018-08-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-30
  • 2018-02-20
相关资源
最近更新 更多