定义带或不带导出的变量

2024-09-30 15:23:00
admin
原创
93
摘要:问题描述:是干什么export用的?以下两者有何区别:export name=value 和name=value 解决方案 1:export使变量可供子流程使用。那是,export name=value 表示变量名称可供您从该 shell 进程运行的任何进程使用。如果您希望某个进程使用此变量,请使用expor...

问题描述:

是干什么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 '...' 中可用!

更多示例请参见:

解决方案 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

在执行期间暂时设置varvalue

command arguments

并将其导出到command子流程,然后将其恢复为先前的值,该值可能是未定义的,或者用不同的(可能是空的)值定义,并且如果之前是这样的,则未导出。)

对于内部、临时或其他文档记录不全的工具,您只需知道它们是否会默默检查其环境。除了一些特定用例(例如将密码或身份验证令牌或其他秘密信息传递给在某种容器或隔离环境中运行的进程)之外,这在实践中很少重要。

如果您真的需要知道并且有权访问源代码,请查找使用getenv系统调用的代码(或者在 Windows 上,请原谅我,变体如getenv_sw_getenv等)。对于某些脚本语言(如 Perl 或 Ruby),请查找ENV。对于 Python,请查找os.environ(但还要注意,egfrom os import environ as foo表示foo现在是 的别名os.environ)。在 Node 中,请查找process.env。对于 C 和相关语言,请查找(但这只是在和之后envp调用 的可选第三个参数的惯例;该语言允许您随意调用它们)。对于 shell 脚本(如上所述),也许要查找具有大写或偶尔混合大小写名称的变量,或者使用实用程序。许多非正式脚本通常在脚本开头附近都有未记录但可发现的赋值;特别是,查找默认赋值参数扩展main`argcargvenv`?=

为了进行简短的演示,这里有一个 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

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/shFreeBSD 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 边界时丢失。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用