如何在脚本中将目录添加到 PATH 以便它影响调用 shell 和会话的其余部分?

如何在脚本中将目录添加到 PATH 以便它影响调用 shell 和会话的其余部分?

我是 Ubuntu 新手。

我编写了一个脚本,用于将目录添加到 PATH 环境。当我运行该脚本时,它运行良好,并且目录被添加到 PATH。但似乎更改只持续到脚本退出,而不是持续整个会话。当我在运行脚本后查看 PATH 时,目录不再存在。有什么建议吗?

答案1

有两件事需要记住:

  1. 命令(包括脚本)在运行期间保持其环境

  2. 命令从父进程继承环境。对于通过 shell 启动的命令,它们将从 shell 继承。

因此,如果您PATH=$PATH:/my/dir这样做,则只会持续脚本的持续时间。要使其永久生效,父 shell 需要知道更改。正确的方法是写入(~/.bashrc如果您使用的是 bash 或适合您的 shell 的 rc 文件)。因此,我们可以使用 >> 附加到文件

echo PATH=$PATH:/my/dir >> ~/.bashrc

当脚本退出时,运行

source ~/.bashrc

这样 shell 就会重新读取配置并注意到更改。现在,您在 shell 中运行的每个命令以及启动的每个新交互式 shell 都将继承新的 PATH 变量

这两个步骤可以放在一起变成一个函数,因为(至少对于 bash 而言)函数在当前 shell 环境中运行,因此与执行source部分脚本不同,source从函数调用将影响当前 shell。

答案2

Sergiy Kolodyazhnyy 的回答确定了这里的问题:脚本在调用它们的 shell 的单独子 shell 中执行,而影响 shell 本身的命令(例如分配给变量或更改工作目录)根本不会影响调用 shell,只会影响子 shell 和(如果它们将export变量传递给环境)它的子 shell。

您可以使用简单的 shell 变量来玩这个游戏......

$ foo=bar
$ echo $foo
bar
$ echo -e "foo=baz \n"'echo $foo' > script
$ cat script
foo=baz 
echo $foo
$ bash script
baz
$ echo $foo
bar

尽管正如 Eliah Kagan 所表明的那样这个答案使用子 shell 更容易做到这一点。

我写这个答案是为了防止你不想永久将目录添加到您的 PATH,但仅限于当前 shell 会话。

要做到这一点你只需要运行脚本在当前 shell 中。这是通过source缩写为.(点)的命令完成的。

给出这个稍微简化的脚本版本...

read -rp "What did you want to add to PATH? "
[ -d "$REPLY" ] && 
PATH="$PATH:$(readlink -m $REPLY)" &&
echo "OK, adding $REPLY to PATH" &&
echo "$PATH" ||
echo "seems like $REPLY is not a directory"

请注意,当我以通常的方式运行脚本时,我会得到与您相同的结果:

$ ./add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

但是当我source运行脚本时它按预期工作:

$ . add-to-path
What did you want to add to PATH? /home/zanna/playground
OK, adding /home/zanna/playground to PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground
$ echo $PATH
/home/zanna/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/zanna/playground

我再补充三点:

  • 我建议将 PATH 分配添加到~/.profile而不是 ,~/.bashrc因为~/.bashrc是由每个交互式 Bash shell(包括从当前 shell 启动的 shell)提供的 - 这意味着子 shell 最终可能会得到非常长的 PATH,因为它们继承了 PATH 并在它们获取 时将其附加到它~/.bashrc。相比之下,~/.profile通常是在登录时(或由登录 shell)获取的。
  • 当您分配给 PATH 时,您不需要这样做,export因为它已经是一个环境变量:从某种意义上说,它已经被导出并且将保持这种状态:分配给 PATH 的变量将始终被子进程继承(但不会是父进程,正如您所发现的!)而无需明确地export进行编辑。
  • 我已经在整个过程中引用了变量REPLYPATH。这是一个好主意,因为其中任何一个都可能有空格或其他触发 shell 扩展的字符。但是,这样做的副作用是~没有扩展,因此脚本很容易返回类似

    looks like ~/some-existing-dir is not a directory
    

    这是真的(从~字面上理解),但帮助不大。也许脚本应该警告用户这一点……

相关内容