在shell脚本中访问在~/.bashrc中设置的变量

4

在非交互式shell的返回之前,我将以下内容放置在我的.bashrc文件的顶部:

FOO="BAR"; export FOO
echo "HELLO WORLD"

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

我有一个脚本test.sh在我的主目录中,内容如下:

#!/bin/bash
echo "A"
echo $FOO
echo "B"

我执行了test.sh脚本。输出如下:

A

B

2个问题:

  • 为什么我看不到$FOO的值?
  • 为什么我看不到“HELLO WORLD”?

编辑: 我以为带有#!/bin/bash的脚本会触发一个子shell,该子shell将再次调用.bashrc,我错了吗?

编辑: 即使我从另一台主机调用脚本,我也看不到任何值。甚至在那种情况下,.bashrc也不会被执行吗?

ssh remotehost "/home/username/test.sh"
4个回答

4

.bashrc 只会自动为非登录交互shell加载。通常情况下,你可以将 . .bashrc 放在 .bash_login 文件的开头,以确保无论是登录还是非登录交互shell都会加载 .bashrc

.bashrc 不会自动为非交互式shell加载,例如在执行shell脚本时启动的shell。

由于你从 .bashrc 导出了 FOO,那么当 test.sh 发现 FOO 的值为空时,我可以确定你是从登录shell中运行该脚本。从提示符号输入 echo $FOO,是否输出 BAR?如果没有,我会感到惊讶。


如果我再次登录,我会看到“HELLO WORLD”,如果我从提示符中执行“echo $FOO”,我会看到“BAR”。如果我在新的登录后执行“test.sh”,它也会打印“BAR”。这意味着如果我从登录shell运行shell脚本,则.bashrc不会被执行。这也意味着shell脚本不会在子shell中运行?如果事情是这么简单的话,我错了很长时间并将接受这个答案。 - Preexo
脚本在子shell中运行;它只是在执行脚本之前不会源.bashrc。在bash中,您可以使用BASH_ENV变量指定在非交互式shell启动时要源的文件。 - chepner

3
当您再次登录时,bashrc会在新的登录shell中运行,并且shell脚本从登录shell继承变量。这就是“环境变量”的含义:它是传递给子进程的变量。
虽然脚本确实在单独的进程中运行,但我不会将其称为“子shell”。 我通常用于像 ( cmd1 ; cmd2 ) 这样的事情,它会创建一个子shell来运行括号中的内容。运行shell脚本会导致解释器的新的 exec,这是一个重要的区别,如下所示:
x=foo # not exported
export y=bar

(
  # This subshell is a copy of the existing shell process, including the
  # non-exported variables, so x is still foo here.
  echo x is $x
  echo y is $y
  # But it's a copy, so modifications do not propagate to the parent.
  x=blah
  y=blah
) | sed 's/^/SUBSHELL: /'

# Back in the parent shell, x and y have not become blah.
echo PARENT: x is $x
echo PARENT: y is $y

# This is not a "subshell" but a new shell. It inherits the exported
# variable y, but not x. Running a shell script resembles this.
sh -c 'echo x is $x ; echo y is $y' | \
sed 's/^/NEWSHELL: /'

非常详细的例子,谢谢。现在变得更清晰了,只剩下一个小不确定性:如果运行一个 shell 脚本意味着运行一个新的解释器 exec,为什么 .bashrc 不会被执行? - Preexo
因为Shell脚本在非交互模式下运行。 - user2404501
好的,我以为 .bashrc 仍然会被调用,但由于顶部的 [ -z "$PS1" ] && return 返回了。哪些操作可以在非交互模式下运行 shell,但仍然触发 .bashrc?抱歉问了这么多小而愚蠢的问题,但我觉得现在必须理解这个。 - Preexo
我不知道为什么会有 [ -z "$PS1" ] && return 这段代码。据我所知,它并不是必要的。你可以从脚本中引用 .bashrc 文件,而 PS1 测试将导致它什么也不做。这不能成为解释......如果你故意在脚本中添加 . ~/.bashrc,那么你可能希望它执行某些操作。也许它存在的目的是允许文件从另一个文件中被引用,该文件可以在交互式 shell 中被引用或作为脚本运行,或者其他一些复杂的情况。或者可能只是当时编写代码的人感到困惑。 - user2404501

1
答案很简单。正确顺序的解决方案:
为什么我看不到“HELLO WORLD”?
请注意,.bashrc 不会在每个命令或启动子 shell 时被解析。它只会在新 shell 上解析。来自 man 页面的内容:
当启动一个非登录交互式 shell 时,如果这些文件存在,bash 会从 /etc/bash.bashrc 和 ~/.bashrc 中读取并执行命令。
所以,我猜您的 .bashrc 不会被处理。您是否尝试在执行脚本之前运行 source ~/.bashrc?另外,您也可以打开一个新终端。
为什么我看不到 $FOO 的值?
如果 bashrc 被解析,则会按预期工作。
一般建议:如果您在shell命令中使用变量,比如echo,请在变量名称周围加上括号,以确保变量的内容不会被视为shell选项。因此,应该这样写:
echo $FOO

应该是

echo "$FOO"

谢谢你的建议...好的,谢谢你的帮助。这意味着在顶部带有#!/bin/bash的shell脚本不会触发.bashrc。这就是为什么我看不到任何输出。但它是否仍然会触发子shell呢? - Preexo

1

根据我的man bash:

如果程序以#!开头,第一行的剩余部分指定程序的解释器。shell在不能处理此可执行文件格式的操作系统上执行指定的解释器。解释器的参数包括程序第一行解释器名称后面的单个可选参数,然后是程序名称,最后是命令参数(如果有)。

因此,我猜一个好的律师可以辩称bash可以解释bash,因此不需要执行另一个解释器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接