定义带或不带导出的变量
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。
$ 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 $var
给 asdf\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 中,float
和 double
在某些 情况下可以互换,但它们仍然是不同的类型。【参考方案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 所说的 subshell 是由进程替换 $() 或 `` 创建的那些,由括号中的命令创建的子 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的底部还是任何地方都可以?以上是关于定义带或不带导出的变量的主要内容,如果未能解决你的问题,请参考以下文章