出口是干什么用的?
有什么区别:
export name=value
and
name=value
出口是干什么用的?
有什么区别:
export name=value
and
name=value
当前回答
作为现有答案的另一个推论,让我们重新表述问题陈述。
“我是否应该导出”的答案与“您的后续代码是否运行隐式访问此变量的命令?”的答案相同。
对于正确记录的标准实用程序,可以在该实用程序手册页的ENVIRONMENT部分中找到答案。例如,git手册页提到GIT_PAGER控制使用哪个实用程序浏览来自git的多页输出。因此,
# XXX FIXME: buggy
branch="main"
GIT_PAGER="less"
git log -n 25 --oneline "$branch"
git log "$branch"
将无法正常工作,因为您没有导出GIT_PAGER。(当然,如果您的系统已经将变量声明为导出到其他地方,则该错误不可重现。)
我们显式地引用了变量$branch,而git程序代码并没有在任何地方引用系统变量分支(它的名字是小写的,这也表明了这一点;但是许多初学者也错误地使用大写字母作为私有变量!有关讨论,请参阅正确的Bash和shell脚本变量大写),因此没有理由导出分支。
正确的代码应该是这样的
branch="main"
export GIT_PAGER="less"
git log -n 25 --oneline "$branch"
git log -p "$branch"
(或者等效地,你可以显式地在每次git调用前加上临时赋值
branch="main"
GIT_PAGER="less" git log -n 25 --oneline "$branch"
GIT_PAGER="less" git log -p "$branch"
如果不是很明显,shell脚本语法
var=value command arguments
的执行期间临时将var设置为value
command arguments
并将其导出到命令子进程,然后将其恢复到之前的值,该值可能是未定义的,或定义为不同的-可能为空的-值,如果之前是这样,则未导出。)
对于内部的、临时的或文档记录不佳的工具,您只需要知道它们是否会默默地检查它们的环境。这在实践中并不重要,除了一些特定的用例之外,例如将密码或身份验证令牌或其他秘密信息传递给运行在某种容器或隔离环境中的进程。
If you really need to know, and have access to the source code, look for code which uses the getenv system call (or on Windows, with my condolences, variations like getenv_s, w_getenv, etc). For some scripting languages (such as Perl or Ruby), look for ENV. For Python, look for os.environ (but notice also that e.g. from os import environ as foo means that foo is now an alias for os.environ). In Node, look for process.env. For C and related languages, look for envp (but this is just a convention for what to call the optional third argument to main, after argc and argv; the language lets you call them anything you like). For shell scripts (as briefly mentioned above), perhaps look for variables with uppercase or occasionally mixed-case names, or usage of the utility env. Many informal scripts have undocumented but discoverable assignments usually near the beginning of the script; in particular, look for the ?= default assignment parameter expansion.
作为一个简短的演示,下面是一个shell脚本,它调用一个Python脚本,该脚本查找$NICKNAME,如果未设置则返回到默认值。
#!/bin/sh
NICKNAME="Handsome Guy"
demo () {
python3 <<\____
from os import environ as env
print("Hello, %s" % env.get("NICKNAME", "Anonymous Coward"))
____
}
demo
# prints "Hello, Anonymous Coward"
# Fix: forgot export
export NICKNAME
demo
# prints "Hello, Handsome Guy"
作为另一个切题的评论,让我重申一下,您只需要导出一个变量一次。很多初学者都喜欢货邪代码
# XXX FIXME: redundant exports
export PATH="$HOME/bin:$PATH"
export PATH="/opt/acme/bin:$PATH"
但通常情况下,您的操作系统已经将PATH声明为export,因此这是更好的编写方式
PATH="$HOME/bin:$PATH"
PATH="/opt/acme/bin:$PATH"
或者重构成
for p in "$HOME/bin" "/opt/acme/bin"
do
case :$PATH: in
*:"$p":*) ;;
*) PATH="$p:$PATH";;
esac
done
# Avoid polluting the variable namespace of your interactive shell
unset p
这避免了在PATH中添加重复的条目。
其他回答
这个答案是错误的,但出于历史目的保留了下来。请看下面的第二篇编辑。
其他人回答说,导出使变量可用于子shell,这是正确的,但只是一个副作用。当你导出一个变量时,它把这个变量放在当前shell的环境中(即shell调用putenv(3)或setenv(3))。 进程的环境通过exec继承,使得变量在子shell中可见。
编辑(5年视角): 这是一个愚蠢的回答。“导出”的目的是使变量“处于随后执行的命令的环境中”,无论这些命令是子shell还是子进程。简单的实现是简单地将变量放在shell环境中,但这将使无法实现export -p。
第二次编辑(又过了5年)。 这个答案很奇怪。也许我曾经有一些理由声称bash将导出的变量放到它自己的环境中,但是这些理由在这里没有给出,现在已经被历史遗忘了。请参见将函数局部变量导出到环境。
需要注意的是,您可以导出一个变量,然后更改其值。变量更改后的值将对子进程可用。一旦为一个变量设置了export,您必须执行export -n <var>来删除该属性。
$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset
有人说在生成子shell时不需要在bash中导出,而其他人则说完全相反。注意子shell(由()、' '、$()或循环创建的子shell)和子进程(按名称调用的进程,例如脚本中出现的bash文本)之间的区别是很重要的。
子shell将可以访问来自父变量的所有变量,而不管它们的导出状态如何。 子进程只能看到导出的变量。
这两种构造的共同之处在于它们都不能将变量传递回父shell。
$ noexport=noexport; export export=export; (echo subshell: $noexport $export; subshell=subshell); bash -c 'echo subprocess: $noexport $export; subprocess=subprocess'; echo parent: $subshell $subprocess
subshell: noexport export
subprocess: export
parent:
还有一个困惑的来源:一些人认为“分叉”子进程是那些看不到非导出变量的子进程。通常fork()后面紧跟着exec(),这就是为什么看起来fork()是要找的东西,而实际上是exec()。你可以先用exec命令运行没有fork()ing的命令,由这个方法启动的进程也不能访问未导出的变量:
$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export
注意,这次我们没有看到parent:行,因为我们已经用exec命令替换了父shell,所以没有任何东西可以执行该命令。
默认情况下,在脚本中创建的变量只对当前shell可用;子进程(子shell)将不能访问已设置或修改的值。允许子进程查看这些值,需要使用export命令。
公认的答案暗示了这一点,但我想明确地说明与shell内置的连接:
如前所述,export将使一个变量对shell和子程序都可用。如果不使用export,该变量将只在shell中可用,并且只有shell内置程序可以访问它。
也就是说,
tango=3
env | grep tango # prints nothing, since env is a child process
set | grep tango # prints tango=3 - "type set" shows `set` is a shell builtin