Bash:为什么“set”的行为不像我预期的那样?

Bash:为什么“set”的行为不像我预期的那样?

我观察到只有导出变量的值通过我的 Mac OS X 附加到我的 PATH 变量.bash_profile。然而,本地设置变量的值则不能。为什么局部变量不能附加到路径中?


“你误解了一些事情,但是什么?把你困惑的代码贴出来。 ——昨天吉尔斯”

请考虑这个片段,我将 MONGODB 变量设置为:

set MONGODB="/usr/local/mongodb/bin"    
export PATH=${PATH}:${MONGODB}

source .bash_profile在航站楼。我看到以下回声:

PATH=~/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:

然而,如果我改为导出 MONGODB 并获取我的.bash_profile,我会看到以下回显:

PATH=~/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/local/mongodb/bin

也许,使用set不当?

答案1

set MONGODB="/usr/local/mongodb/bin"

这不是变量赋值。 (它是 C shell(csh、tcsh)中的一个,但不是 Bourne 风格 shell(sh、ash、bash、ksh、zsh 等)中的一个。)这是对内置函数的调用set,它设置位置参数,即$1$2等。尝试在终端中运行此命令,然后echo $1

要将值赋给 shell 变量,只需编写

MONGODB="/usr/local/mongodb/bin"

这将创建一个 shell 变量(也称为(命名)参数),您可以使用$MONGODB.该变量仍然保留在 shell 内部,除非您使用 .export 将其导出export MONGODB。如果导出,该变量对于该 shell 启动的所有进程也是可见的,通过环境。您可以将作业和导出压缩为一行:

export MONGODB="/usr/local/mongodb/bin"

对于您正在做的事情,似乎不需要MONGODB脚本外部,并且PATH已经导出(导出变量后,如果分配新值,它将反映在环境中)。所以你可以写:

MONGODB="/usr/local/mongodb/bin"    
PATH=${PATH}:${MONGODB}

答案2

我将尝试解释 shell 变量是如何工作的。将局部变量附加到像 PATH 这样的环境变量中是绝对可能的。

每个正在运行的进程都有一个环境变量列表。它们是名称=值对。当创建一个新进程时叉()它继承了这些变量(以及其他变量,如打开的文件、用户 ID 等)。相反,shell 变量是一个内部 shell 概念。创建新进程时不会继承它们。您可以导出 shell 变量并将其设为环境变量。当您在 shell 脚本中编写时,FOO='bar'它是一个 shell 变量。您可以尝试创建 2 个脚本:

# test1.sh
FOO='bar'
sh test2.sh

# test2.sh
echo "${FOO}"

当您执行第一个脚本时,它会设置一个内部 shell 变量,然后调用 fork()。父 shell 进程将等待()为了让子进程完成,然后继续执行(如果有更多命令)。在子进程中执行()被调用来加载新的 shell。这个新进程不知道FOO。如果修改第一个脚本:

# test1.sh
export FOO='bar'
sh test2.sh

FOO 变量成为环境的一部分并继承到分叉进程。值得注意的是,环境并不是全球性的。子进程不能影响其父进程的环境变量。

# test3.sh
sh test4.sh
echo "${PATH}"

# test4.sh
export PATH="${PATH}:/new/path"

test4.sh 中的修改在 test3.sh 中不可见。信息根本就不是这样的。当子进程结束时,其环境将被丢弃。让我们更改test3.sh:

# test3.sh
source test4.sh
echo "${PATH}"

Source 是一个内置的 shell 命令。它告诉 shell 打开一个文件,然后读取并执行其内容。只有一个 shell 进程。这样调用者就可以看到环境变量甚至shell变量的修改。

您可能知道 PATH 是一个特殊的环境变量,它告诉 shell 在哪里查找其他可执行文件。当新的登录 shell 启动时,它会自动获取 .bash_profile。在那里声明的变量将是可见的。但是,如果在 .bash_profile 中使用 sh 调用其他脚本,则您在这些脚本中设置的 PATH 将丢失。

答案3

也许,使用“set”是不合适的?

是的,这就是你的问题。set没有做你可能期望的事情。来自文档:

这个内置函数非常复杂,值得单独专门一节。set允许您更改 shell 选项的值并设置位置参数,或显示 shell 变量的名称和值。

请注意,它所做的事情列表中明显缺少“实际设置 shell 变量”。埋藏在所有文档中,你会发现它所做的是设置 shell位置参数对于你给出的论点。你只是给出一个论点,全部MONGODB="/usr/local/mongodb/bin"。因此$1被设置为该值(并且$#被设置为 1,因为只有一个参数)。

反助记 Unix 命令名称得一分,是吧?

所以无论如何,尝试一下:

MONGODB=/usr/local/mongodb/bin
export PATH=${PATH}:${MONGODB}

它会起作用的。

相关内容