export
使变量可用于子流程。
那是,
export name=value
表示变量名称可用于您从该 shell 进程运行的任何进程。如果您希望某个进程使用此变量,请使用 export
,然后从该 shell 运行该进程。
name=value
表示变量作用域仅限于 shell,不能用于任何其他进程。您可以将其用于(例如)循环变量、临时变量等。
请务必注意,导出变量不会使其可用于父进程。也就是说,在衍生进程中指定和导出变量不会使其在启动它的进程中可用。
为了说明其他答案在说什么:
$ foo="Hello, World"
$ echo $foo
Hello, World
$ bar="Goodbye"
$ export foo
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar
bash-3.2$
al$ foobar="Whatever" bash
的另一个示例
这个答案是错误的,但出于历史目的而保留。请参阅下面的第二次编辑。
其他人回答说 export 使变量可用于子shell,这是正确的,但只是副作用。当您导出一个变量时,它会将该变量放入当前 shell 的环境中(即 shell 调用 putenv(3)
或 setenv(3)
)。
进程的环境跨 exec 继承,使变量在子 shell 中可见.
编辑(以 5 年的视角):这是一个愚蠢的答案。 “导出”的目的是使变量“在随后执行的命令的环境中”,无论这些命令是子外壳还是子进程。一个简单的实现是将变量简单地放在 shell 的环境中,但这会使 export -p
无法实现。
第二次编辑(又过了 5 年)。 这个答案很奇怪。也许我曾经有一些理由声称 bash 将导出的变量放入它自己的环境中,但是这些原因在这里没有给出,现在已经被历史遗忘了。请参阅Exporting a function local variable to the environment。
bash
中,export 确实将变量添加到当前 shell 的环境中,但 dash
不是这种情况。在我看来,将变量添加到当前 shell 的环境中是实现 export
语义的最简单方法,但这种行为不是强制性的。
dash
与此有什么关系。原始发帖人专门询问bash
。
bash
,但同样适用于任何 bourne-shell 变体。过于具体并提供仅适用于 bash
的答案是一种大恶。
bash
是 shell 的 jQuery。
export makes the variable available to subshells, and that is correct
这是一个非常混乱的术语用法。子shell 不需要 export
来继承变量。子流程可以。
有人说生成子shell时不必在bash中导出,而其他人则完全相反。重要的是要注意子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()s 之后紧跟 exec()s,这就是为什么看起来 fork() 是要寻找的东西,而实际上它是 exec()。您可以在没有 fork() 的情况下首先使用 exec
命令运行命令,并且由此方法启动的进程也将无法访问未导出的变量:
$ noexport=noexport; export export=export; exec bash -c 'echo execd process: $noexport $export; execd=execd'; echo parent: $execd
execd process: export
请注意,这次我们没有看到 parent:
行,因为我们已经用 exec
命令替换了父 shell,所以没有任何东西可以执行该命令。
&
) 还会创建一个子外壳。
var=asdf bash -c 'echo $var'
或 var=asdf exec bash -c 'echo $var'
怎么样?输出为 asdf
。如果放在变量定义之后,;
会有所不同。会有什么解释?看起来 var
(没有 ;
)以某种方式与生成的子进程有关,因为原始 shell 与它无关。 echo $var
如果在第二行执行,则不打印任何内容。但是一排 var=asdf bash -c 'echo $var'; echo $var
给出 asdf\nasdf
。
var=value command
在命令 command
的持续时间内,在该命令的环境中将变量 var
设置为值 value
。这有点类似于 env
命令的作用。
export NAME=value
用于对子流程有意义的设置和变量。
NAME=value
用于当前 shell 进程私有的临时变量或循环变量。
更详细地说,export
标记环境中的变量名称,该变量名称在创建时复制到子流程及其子流程。从未从子流程中复制回任何名称或值。
一个常见的错误是在等号周围放置一个空格: $ export FOO = "bar" bash: export: `=': not a valid identifier
子进程只能看到导出的变量(B):$ A="Alice";出口 B="鲍勃"; echo "回声 A 是 \$A。B 是 \$B" |重击 A 是 . B 是鲍勃
子进程的变化不会改变主shell: $ export B="Bob";回声'B =“香蕉”' |重击;回声 $B 鲍勃
标记为导出的变量在创建子流程时复制了值: $ export B="Bob"; echo '(sleep 30; echo "子进程 1 有 B=$B")' |重击 & [1] 3306 $ B="香蕉"; echo '(sleep 30; echo "子进程 2 有 B=$B")' | bash 子进程 1 有 B=Bob 子进程 2 有 B=Banana [1]+ 完成 echo '(sleep 30; echo "Subprocess 1 has B=$B")' |重击
只有导出的变量成为环境的一部分(man environ): $ ALICE="Alice";出口鲍勃=“鲍勃”;环境 | grep "爱丽丝\|鲍勃" BOB=鲍勃
所以,现在它应该像夏天的太阳一样清晰!感谢 Brain Agnew、alexp 和 William Prusell。
应该注意的是,您可以导出一个变量,然后再更改该值。变量的更改值将可用于子进程。为变量设置导出后,您必须执行 export -n <var>
以删除该属性。
$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset
export
将使变量可用于从当前 shell 派生的所有 shell。
Makefile
中的@leolehu export
通常是一个单独的问题。 Quick experimentation 表明,可以预见的是,export
的位置并不重要。 (可以预测,因为 make
将解析和处理整个 Makefile
,然后然后尝试解析依赖关系以确定要运行的目标。)
您可能已经知道,UNIX 允许进程拥有一组环境变量,它们是键/值对,键和值都是字符串。操作系统负责为每个进程分别保存这些对。
程序可以通过这个 UNIX API 访问它的环境变量:
char *getenv(const char *name);
int setenv(const char *name, const char *value, int override);
int unsetenv(const char *name);
进程还从父进程继承环境变量。操作系统负责在创建子进程时创建所有“envar”的副本。
Bash 和其他 shell 一样,能够根据用户请求设置其环境变量。这就是 export
的存在。
export
是为 Bash 设置环境变量的 Bash 命令。使用此命令设置的所有变量都将被此 Bash 创建的所有进程继承。
Bash 中的另一种变量是内部变量。由于 Bash 不仅仅是交互式 shell,它实际上是一个脚本解释器,与任何其他解释器(例如 Python)一样,它能够保留自己的一组变量。应该提到的是,Bash(与 Python 不同)仅支持字符串变量。
定义 Bash 变量的符号是 name=value
。这些变量保留在 Bash 内部,与操作系统保存的环境变量无关。
详细了解 Shell Parameters(包括变量)
另外值得注意的是,根据 Bash 参考手册:
任何简单命令或函数的环境都可以通过在其前面加上参数分配来临时扩充,如 Shell 参数中所述。这些赋值语句只影响该命令看到的环境。
总结一下:
export 用于在操作系统中设置环境变量。此变量将可用于当前 Bash 进程创建的所有子进程。
bash 变量表示法 (name=value) 用于设置仅对当前 bash 进程可用的局部变量
为另一个命令添加前缀的 Bash 变量表示法仅为该命令的范围创建环境变量。
A="string"
这样的公共变量实际上与 A[0]="string"
相同。事实上,在说出 A="string"
之后,您可以使用 A[1]="string2"
、A+=(string3 string4 "string 5 is longer")
然后 echo "${A[@]}"
将更多字符串连接到 1 字符串数组以打印它们。请注意,它需要将数组提供给 printf
命令之类的东西,以便在字符串之间获得某种分隔符,因为默认值是空格,而 string5 包含空格。
export a b; a=this; b[0]=that; env | grep ^[ab]
不相同。在 C/C++/Java 中,float
和 double
在某些 情况下可以互换,但它们仍然是不同的类型。
accepted answer 暗示了这一点,但我想明确说明与 shell 内置函数的连接:
如前所述,export
将使 shell 和子级都可以使用一个变量。如果 not 使用 export
,则该变量将仅在 shell 中可用,并且只有 shell builtins 可以访问它。
那是,
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
UNIX 的两位创建者 Brian Kernighan 和 Rob Pike 在他们的《UNIX 编程环境》一书中对此进行了解释。谷歌搜索标题,你会很容易找到一个 pdf 版本。
它们在第 3.6 节中介绍了 shell 变量,并在该节的末尾重点介绍了 export
命令的使用:
当您想让变量的值在子 shell 中可访问时,应该使用 shell 的 export 命令。 (您可能会想为什么无法将变量的值从子 shell 导出到其父 shell)。
这是另一个例子:
VARTEST="value of VARTEST"
#export VARTEST="value of VARTEST"
sudo env | grep -i vartest
sudo echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"
sudo bash -c 'echo ${SUDO_USER} ${SUDO_UID}:${SUDO_GID} "${VARTEST}"'
只有通过使用 export VARTEST,VARTEST 的值才能在 sudo bash -c '...' 中获得!
有关更多示例,请参见:
http://mywiki.wooledge.org/SubShell
bash-hackers.org/wiki/doku.php/scripting/processtree
只是为了显示在环境 (env) 中的导出变量和不在环境中的非导出变量之间的区别:
如果我这样做:
$ MYNAME=Fred
$ export OURNAME=Jim
然后只有 $OURNAME 出现在环境中。变量 $MYNAME 不在环境中。
$ env | grep NAME
OURNAME=Jim
但是变量 $MYNAME 确实存在于 shell 中
$ echo $MYNAME
Fred
默认情况下,在脚本中创建的变量只对当前 shell 可用;子进程(子外壳)将无法访问已设置或修改的值。允许子进程查看值,需要使用 export 命令。
尽管在讨论中没有明确提到,但从 bash 内部生成子 shell 时没有必要使用 export,因为所有变量都被复制到子进程中。
作为此处现有答案的另一个推论,让我们重新表述问题陈述。
“我应该export
”的答案与“您的后续代码是否运行隐式访问此变量的命令?”问题的答案相同。
对于正确记录的标准实用程序,可以在该实用程序手册页的 ENVIRONMENT 部分找到此问题的答案。因此,例如,git
manual page 提到 GIT_PAGER
控制使用哪个实用程序来浏览来自 git
的多页输出。因此,
# XXX FIXME: buggy
branch="main"
GIT_PAGER="less"
git log -n 25 --oneline "$branch"
git log "$branch"
将无法正常工作,因为您没有 export GIT_PAGER
。 (当然,如果您的系统已经将变量声明为在其他地方导出,则该错误是不可重现的。)
我们明确引用了变量 $branch
,而 git
程序代码在任何地方都没有引用系统变量 branch
(正如它的名称写在小写;但许多初学者也错误地使用大写作为私有变量!参见 Correct Bash and shell script variable capitalization 进行讨论),因此没有理由使用 export branch
。
正确的代码看起来像
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
并将其导出到 command
子进程,然后将其恢复为先前的值,该值可能是未定义的,或者使用不同的(可能为空的)值定义,如果是之前的值,则未导出。)
对于内部、临时或其他记录不充分的工具,您只需要知道它们是否默默地检查其环境。这在实践中很少重要,除了一些特定的用例之外,例如将密码或身份验证令牌或其他秘密信息传递给在某种容器或隔离环境中运行的进程。
如果您确实需要了解并有权访问源代码,请查找使用 getenv
系统调用的代码(或在 Windows 上,我表示哀悼,getenv_s
、w_getenv
等变体)。对于某些脚本语言(例如 Perl 或 Ruby),请查找 ENV
。对于 Python,查找 os.environ
(但也请注意,例如 from os import environ as foo
意味着 foo
现在是 os.environ
的别名)。在节点中,查找 process.env
。对于 C 和相关语言,请查找 envp
(但这只是在 argc
和 argv
之后调用 main
的可选第三个参数的约定;该语言允许您随意调用它们) .对于 shell 脚本(如上所述),也许寻找具有大写或偶尔混合大小写名称的变量,或者使用实用程序 env
。许多非正式脚本通常在脚本开头附近有未记录但可发现的任务;特别是,寻找 ?=
默认分配 parameter expansion。
对于一个简短的演示,这里是一个 shell 脚本,它调用一个查找 $NICKNAME
的 Python 脚本,如果未设置,则回退到默认值。
#!/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"
作为另一个切题的话,让我重申一下,您只需要 export
一个变量一次。许多初学者货物崇拜代码喜欢
# XXX FIXME: redundant exports
export PATH="$HOME/bin:$PATH"
export PATH="/opt/acme/bin:$PATH"
但通常情况下,您的操作系统已经将 PATH
声明为已导出,因此最好这样写
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
添加重复条目。
不定期副业成功案例分享
name=value command
确实 使变量在子流程command
中可用。