定义带或不带导出的变量

Posted

技术标签:

【中文标题】定义带或不带导出的变量【英文标题】:Defining a variable with or without export 【发布时间】:2010-11-12 14:04:32 【问题描述】:

export 是干什么用的?

有什么区别:

export name=value

name=value

【问题讨论】:

另外注意export name=value 不可移植。根据您的具体需求,尝试name=value; export name 以获得便携式解决方案。 另见:***.com/questions/68529921/… 【参考方案1】:

其他人回答说 export 使变量可用于子shell,这是正确的,但只是副作用。当您导出一个变量时,它会将这个变量放入当前 shell 的环境中(即 shell 调用 putenv(3)setenv(3))。 进程的环境跨 exec 继承,使变量在子 shell 中可见。

编辑(以 5 年的视角):这是一个愚蠢的答案。 “导出”的目的是使变量“在随后执行的命令的环境中”,无论这些命令是子外壳还是子进程。一个幼稚的实现是简单地将变量放在 shell 的环境中,但这将导致无法实现 export -p

【讨论】:

请注意,这并不完全正确。在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 来继承变量。子流程可以。【参考方案2】:

据说生成子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()。您可以使用exec 命令在没有 fork() 的情况下运行命令,并且由此方法启动的进程也将无法访问未导出的变量:

$ 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的循环; OTOH 管道确实(总是用于最后一个以外的部分,有时用于最后一个,具体取决于您的外壳、版本和选项)。后台 (&) 也会创建一个子 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 $varasdf\nasdf @4xy 这是完全不同的情况;在该命令的环境中,var=value command 在命令command 的持续时间内将变量var 设置为值value。这有点类似于env 命令的作用。【参考方案3】:

为了说明其他答案的意思:

$ 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 以防万一有人想在 Bash 中用数组尝试这个(就像我做的那样......)然后单挑:it can't be done。【参考方案4】:

accepted answer 暗示了这一点,但我想明确说明与 shell 内置函数的连接:

如前所述,export 将使 shell 和子级都可以使用一个变量。如果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

【讨论】:

【参考方案5】:

需要注意的是,您可以导出一个变量,然后再更改该值。变量的更改值将可用于子进程。为变量设置导出后,您必须执行 export -n <var> 以删除该属性。

$ K=1
$ export K
$ K=2
$ bash -c 'echo $K-unset'
2
$ export -n K
$ bash -c 'echo $K-unset'
unset

【讨论】:

谢谢,这正是我正在寻找的信息,因为我看到了一个使用环境变量的脚本,然后用新值“重新导出”它们,我想知道是否有必要.【参考方案6】:

UNIX 的两位创建者 Brian Kernighan 和 Rob Pike 在他们的《UNIX 编程环境》一书中对此进行了解释。谷歌搜索标题,你会很容易找到一个 pdf 版本。

它们在第 3.6 节中介绍了 shell 变量,并重点介绍了该节末尾的 export 命令的使用:

当你想让一个变量的值在子 shell 中可访问时,应该使用 shell 的 export 命令。 (你可能会想为什么无法将变量的值从子 shell 导出到其父 shell)。

【讨论】:

【参考方案7】:

您可能已经知道,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 创建的所有进程继承。

更多关于Environment in Bash

Bash 中的另一种变量是内部变量。由于 Bash 不仅仅是交互式 shell,它实际上是一个脚本解释器,与任何其他解释器(例如 Python)一样,它能够保留自己的一组变量。需要指出的是,Bash(与 Python 不同)仅支持字符串变量。

定义 Bash 变量的符号是name=value。这些变量留在 Bash 内部,与操作系统保存的环境变量无关。

更多关于Shell Parameters(包括变量)

另外值得注意的是,根据 Bash 参考手册:

可以扩充任何简单命令或功能的环境 暂时通过在其前面加上参数分配,如所述 在Shell Parameters。这些赋值语句只影响 该命令看到的环境。


总结一下:

export 用于在操作系统中设置环境变量。此变量将可用于当前 Bash 进程创建的所有子进程。 bash 变量表示法 (name=value) 用于设置仅对当前 bash 进程可用的局部变量 为另一个命令添加前缀的 Bash 变量表示法仅为该命令的范围创建环境变量。

【讨论】:

bash vars 不支持像 Python 那么多的类型,但确实有字符串、整数和两种数组('indexed'/traditional 和 'associative',类似于 awk 数组,perl哈希或 Python 字典)。其他贝壳有所不同;只有字符串是可移植的 @dave_thompson_085 - 实际上,所有这些都存储为字符串数组,并在必要时自动转换为算术等。像A="string" 这样的公共变量实际上与A[0]="string" 相同。事实上,在说出A="string" 之后,您可以使用A[1]="string2"A+=(string3 string4 "string 5 is longer") 然后echo "$A[@]" 将更多字符串连接到1 字符串数组以打印它们。请注意,它需要像将数组提供给 printf 命令这样的操作,以便在字符串之间获得某种分隔符,因为默认值是空格,而 string5 包含空格。 @DocSalvager: export a b; a=this; b[0]=that; env | grep ^[ab] 并不相同。在 C/C++/Java 中,floatdouble某些 情况下可以互换,但它们仍然是不同的类型。【参考方案8】:

默认情况下,在脚本中创建的变量只对当前 shell 可用;子进程(子外壳)将无法访问已设置或修改的值。允许子进程查看值,需要使用导出命令。

【讨论】:

【参考方案9】:

只是为了显示在环境 (env) 中的导出变量和不在环境中的非导出变量之间的区别:

如果我这样做:

$ MYNAME=Fred
$ export OURNAME=Jim

然后只有 $OURNAME 出现在环境中。变量 $MYNAME 不在环境中。

$ env | grep NAME
OURNAME=Jim

但变量 $MYNAME 确实存在于 shell 中

$ echo $MYNAME
Fred

【讨论】:

您好 请问,我可以在变量声明之前导出一个变量吗?比如 export OURNAME 然后 OURNAME=Jim? @leoleohu 如果您在分配 OURNAME 之前将其导出,您将只导出一个空字符串。 @ingernet 即使您在分配变量之前导出变量,在调用子进程之前分配给该变量的任何值都将被子进程看到。但是一旦子进程被调用,在父进程中对导出变量所做的任何更新都不会被子进程看到,这是因为在进程“exec”调用期间,变量是按值复制的【参考方案10】:

export 使变量可用于子进程。

也就是说,

export name=value

表示变量名称可用于您从该 shell 进程运行的任何进程。如果您希望某个进程使用此变量,请使用export,然后从该 shell 运行该进程。

name=value

表示变量范围仅限于 shell,不能用于任何其他进程。您可以将其用于(例如)循环变量、临时变量等。

请务必注意,导出变量不会使其对父进程可用。也就是说,在衍生进程中指定和导出变量不会使其在启动它的进程中可用。

【讨论】:

特别是 export 使变量可以通过环境提供给子进程。 我还要补充一点,如果导出位于您“来源”的文件中(例如 .filename),那么它也会将其导出到您的工作环境中。 @rogerdpack 你不能在没有导出的情况下做到这一点吗?猫 > 等等 \n a=hi \n 。废话;回声$a;为我输出“hi”。 很好,即使没有导出也能正常工作。所以我想在获取文件时,如果你使用 export 它将反映在子进程中,如果你不这样做只会影响本地 bash 环境...... 这有一个极端情况; name=value command确实使变量在子进程command中可用。【参考方案11】:

虽然在讨论中没有明确提到,但从 ​​bash 内部生成子 shell 时没有必要使用 export,因为所有变量都被复制到子进程中。

【讨论】:

请解释一下,因为您所说的似乎与上述示例的答案直接矛盾。 如果您不希望全局导出变量但仅可用于子进程,这是正确的方法!谢谢。 @MikeLippert Scott 所说的 subshel​​l 是由进程替换 $() 或 `` 创建的那些,由括号中的命令创建的子 shell (command1; command2) 等自动继承所有父 shell 的变量,即使它们'不出口。但是调用的子进程或脚本不会看到所有的 shell 变量,除非它们被导出。这是主要区别之一,经常被误解 @Pavan 啊,这很有帮助。因为通过调用新的 bash 进程创建的子 shell 不是他的意思,只会接收导出的变量。这就是我多年前问这个问题时的想法。【参考方案12】:

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。

【讨论】:

【参考方案13】:

这是另一个例子:

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 才能在 sudo bash -c '...' 中获得 VARTEST 的值!

更多示例参见:

http://mywiki.wooledge.org/SubShell

bash-hackers.org/wiki/doku.php/scripting/processtree

【讨论】:

【参考方案14】:

export 将使该变量可用于从当前 shell 派生的所有 shell。

【讨论】:

嗨约翰,你知道这个出口的位置是否重要吗?我应该把它放在makefile的底部还是任何地方都可以?

以上是关于定义带或不带导出的变量的主要内容,如果未能解决你的问题,请参考以下文章

在 ES6 中导入带或不带大括号 [重复]

使用带或不带 cognito 的 aws api 网关的联合身份的轻量级 rbac

带或不带`.min`的样式表参考

使用带或不带括号的 python 装饰器

带或不带存储库的 NHibernate

-XX:MaxPermSize 带或不带 -XX:PermSize