定义带或不带导出的变量
- 2024-09-30 15:23:00
- admin 原创
- 93
问题描述:
是干什么export
用的?
以下两者有何区别:
export name=value
和
name=value
解决方案 1:
export
使变量可供子流程使用。
那是,
export name=value
表示变量名称可供您从该 shell 进程运行的任何进程使用。如果您希望某个进程使用此变量,请使用export
,然后从该 shell 运行该进程。
name=value
意味着变量范围仅限于 shell,其他进程无法使用。您可以将其用于(例如)循环变量、临时变量等。
需要注意的是,导出变量并不会使其可供父进程使用。也就是说,在衍生进程中指定和导出变量并不会使其在启动它的进程中可用。
解决方案 2:
为了说明其他答案的意思:
$ export foo="Hello, World"
$ bar="Goodbye"
$ echo $foo
Hello, World
$ echo $bar
Goodbye
$ bash
bash-3.2$ echo $foo
Hello, World
bash-3.2$ echo $bar
bash-3.2$
解决方案 3:
有人说在生成子 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()。您可以在不先使用命令 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,所以没有什么可以执行该命令。
解决方案 4:
这个答案是错误的,但出于历史目的保留了下来。请参阅下面的第二次编辑。
其他人回答说,export 使变量可供子 shell 使用,这是正确的,但只是副作用。当您导出变量时,它会将该变量放入当前 shell 的环境中(即 shell 调用putenv(3)
或setenv(3)
)。
进程的环境在 exec 之间继承,使变量在子 shell 中可见。
编辑(以 5 年的眼光来看):
这个答案很愚蠢。'export' 的目的是使变量“处于随后执行的命令的环境中”,无论这些命令是子 shell 还是子进程。一个简单的实现是简单地将变量放在 shell 的环境中,但这将使其无法实现export -p
。
第二次编辑(又过了 5 年)。
这个答案简直太奇怪了。也许我曾经有理由声称 bash 将导出的变量放入其自己的环境中,但这些理由没有在这里给出,现在已成为历史。请参阅将函数局部变量导出到环境。
解决方案 5:
export NAME=value
对于子流程有意义的设置和变量。
NAME=value
用于当前 shell 进程私有的临时变量或循环变量。
更详细地说,export
在创建时标记复制到子进程及其子进程的环境中变量名称。不会从子进程复制回任何名称或值。
一个常见的错误是在等号两边加空格:
$ export FOO = "bar"
bash: export: `=': not a valid identifier
B
子进程只能看到导出的变量( ):
$ A="Alice"; export B="Bob"; echo "echo A is $A. B is $B" | bash
A is . B is Bob
子进程中的更改不会改变主 shell:
$ export B="Bob"; echo 'B="Banana"' | bash; echo $B
Bob
标记为导出的变量在创建子进程时会复制其值:
$ export B="Bob"; echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash &
[1] 3306
$ B="Banana"; echo '(sleep 30; echo "Subprocess 2 has B=$B")' | bash
Subprocess 1 has B=Bob
Subprocess 2 has B=Banana
[1]+ Done echo '(sleep 30; echo "Subprocess 1 has B=$B")' | bash
只有导出的变量才会成为环境的一部分(
man environ
):
$ ALICE="Alice"; export BOB="Bob"; env | grep "ALICE|BOB"
BOB=Bob
所以,现在应该像夏日的阳光一样晴朗!感谢 Brain Agnew、alexp 和 William Prusell。
解决方案 6:
需要注意的是,您可以导出变量,然后更改其值。变量的更改值将可供子进程使用。一旦为变量设置了导出,您必须export -n <var>
删除该属性。
$ K=1
$ export K
$ K=2
$ bash -c 'echo ${K-unset}'
2
$ export -n K
$ bash -c 'echo ${K-unset}'
unset
解决方案 7:
export
将使该变量可用于从当前 shell 分叉的所有 shell。
解决方案 8:
您可能已经知道,UNIX 允许进程拥有一组环境变量,这些变量是键/值对,键和值都是字符串。操作系统负责为每个进程分别保存这些对。
程序可以通过以下 UNIX API 访问其环境变量:
char *getenv(const char *name);
int setenv(const char *name, const char *value, int override);
int unsetenv(const char *name);
进程还会从父进程继承环境变量。操作系统负责在子进程创建时创建所有“envar”的副本。
与其他 shell 一样, Bash能够根据用户请求设置其环境变量。这就是它export
存在的原因。
export
是用于设置 Bash 环境变量的 Bash 命令。使用此命令设置的所有变量将被此 Bash 创建的所有进程继承。
有关Bash 中的环境的更多信息
Bash 中的另一种变量是内部变量。由于 Bash 不仅仅是交互式 shell,它实际上是一个脚本解释器,与任何其他解释器(例如 Python)一样,它能够保留自己的一组变量。需要指出的是,Bash(与 Python 不同)仅支持字符串变量。
定义 Bash 变量的符号是name=value
。这些变量位于 Bash 内部,与操作系统保存的环境变量无关。
有关Shell 参数(包括变量)的更多信息
还值得注意的是,根据 Bash 参考手册:
任何简单命令或函数的环境都可以通过在它前面加上参数赋值来临时增强,如Shell 参数中所述。这些赋值语句仅影响该命令所见的环境。
总结一下:
export
用于设置操作系统中的环境变量。此变量将对当前 Bash 进程创建的所有子进程可用。Bash 变量表示法(名称=值)用于设置仅对 bash 当前进程可用的局部变量
Bash 变量符号作为另一个命令的前缀,仅为该命令的范围创建环境变量。
解决方案 9:
接受的答案暗示了这一点,但我想明确与 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
解决方案 10:
UNIX 的两位创始人 Brian Kernighan 和 Rob Pike 在他们的著作《UNIX 编程环境》中对此进行了解释。在 Google 中搜索书名,您就能轻松找到 pdf 版本。
export
它们在第 3.6 节中讨论了 shell 变量,并在该节末尾重点介绍了命令的使用:
当您想使变量的值在子 shell 中可访问时,应使用 shell 的 export 命令。(您可能会想为什么没有办法将变量的值从子 shell 导出到其父级)。
解决方案 11:
这是另一个例子:
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 '...' 中可用!
更多示例请参见:
bash-hackers.org/wiki/doku.php/scripting/processtree
解决方案 12:
仅显示环境(env)中的导出变量和不在环境中的非导出变量之间的区别:
如果我这样做:
$ MYNAME=Fred
$ export OURNAME=Jim
那么环境中只会出现 $OURNAME。变量 $MYNAME 不在环境中。
$ env | grep NAME
OURNAME=Jim
但变量 $MYNAME 在 shell 中确实存在
$ echo $MYNAME
Fred
解决方案 13:
默认情况下,脚本中创建的变量仅对当前 shell 可用;子进程(子 shell)将无法访问已设置或修改的值。要允许子进程查看这些值,需要使用 export 命令。
解决方案 14:
作为此处现有答案的另一个推论,让我们重新表述问题陈述。
“我应该吗”的答案与“您后续的代码是否运行隐式export
访问该变量的命令?”这个问题的答案相同。
对于有正确文档记录的标准实用程序,可以在实用程序手册页的“环境”部分中找到答案。因此,例如,git
手册页提到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
(从其名称小写的事实也可以看出这一点;但许多初学者也错误地将私有变量大写!请参阅正确的 Bash 和 shell 脚本变量大写以了解讨论),所以没有理由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
(但还要注意,egfrom os import environ as foo
表示foo
现在是 的别名os.environ
)。在 Node 中,请查找process.env
。对于 C 和相关语言,请查找(但这只是在和之后envp
调用 的可选第三个参数的惯例;该语言允许您随意调用它们)。对于 shell 脚本(如上所述),也许要查找具有大写或偶尔混合大小写名称的变量,或者使用实用程序。许多非正式脚本通常在脚本开头附近都有未记录但可发现的赋值;特别是,查找默认赋值参数扩展。main
`argcargv
env`?=
为了进行简短的演示,这里有一个 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"
另一个离题的评论是,让我重申一下,你只需要使用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
。
解决方案 15:
尽管在讨论中没有明确提到,但是从 bash 内部生成子 shell 时没有必要使用 export,因为所有变量都被复制到子进程中。
解决方案 16:
Git 2.47(2024 年第四季度),第 1 批export VAR
说明了和之间的另一个区别VAR=VAL
:"VAR=VAL shell_func"
应该避免。
参见Junio C Hamano ( )的提交 728a196 (2024 年 7 月 22 日) 。(由Junio C Hamano -- --于 2024 年 7 月 31 日的提交 ca9221c中合并)gitster
CodingGuidelines
:记录“失败”的 shell “VAR=VALshell_func"
帮助者:Kyle Lippincott
多年来,我们积累了社区智慧,避免使用常见的“一次简短导出”结构来处理 shell 函数,但似乎已经忘记了它在哪个平台上会失败。
现在,在调查最近一个主题的故障时,我们发现了一个 shell 失败的例子。
让我们记录下来。
这并不意味着一旦 Ubuntu 20.04 退役,我们就可以自由地开始使用该构造。
但这确实意味着,在 Ubuntu 20.04 从相关机器上完全退役之前,我们不能使用该构造。
此外,posix 明确表示,构造的行为未指定。
CodingGuidelines
现在在其手册页中包含:
共同结构
VAR=VAL command args
仅在“命令参数”运行时临时设置和导出环境变量 VAR 很方便,但当用于非外部命令(如shell 函数)时,根据 POSIX 会触发未指定的行为。
确实,在这种情况下,Ubuntu 20.04 上的 dash 0.5.10.2-6、
/bin/sh
FreeBSD 13 上的 dash 0.5.10.2-6 和 AT&T ksh 都会进行临时分配而不导出变量。由于它无法在各个 shell 之间移植,因此请不要对 shell 函数使用此语法。
一个常见的解决方法是在子 shell 中执行显式导出,如下所示:
# (incorrect) VAR=VAL func args (correct) ( VAR=VAL && export VAR && func args )
但要小心,“ ”对当前 shell 中的变量的影响
func
将会在跨越子 shell 边界时丢失。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件