设置环境变量的最佳发行版/外壳不可知方式是什么?

str*_*gee 35 shell login environment-variables

问题说明了一切。我目前使用 Arch Linux 和 zsh,但我想要一个(至少)既适用于 VT 又适用于 xterm 的解决方案,并且(希望最好)在我切换发行版或 shell 时仍能继续工作。

我在不同发行版的文档中听到了对这个问题截然不同的答案。Ubuntu 说“使用 .pam_environment”。我认为在 Arch 中,他们推荐的内容取决于您的外壳。目前,我将所有内容都放在.profile 中,如果由于某种原因 shell 没有提供它(例如,如果 .bash_profile 存在则为 bash),我会通过手动获取它来覆盖它。但似乎必须有更好的方法。

Gil*_*il' 33

不幸的是,没有完全可移植的位置来设置环境变量。最接近的两个文件是~/.profile,这是传统位置,可在许多设置中开箱即用,而~/.pam_environment是现代、常见但有限的替代方案。

放什么 ~/.pam_environment

该文件~/.pam_environment是由所有的登录方法,其使用读取PAM和启用了此文件。这涵盖了当今大多数 Linux 系统。

的主要优点~/.pam_environment是(启用时)它在用户的 shell 启动之前被读取,因此无论会话类型、登录 shell 和其他复杂性如何,它都可以工作。它甚至适用于非交互式登录,例如su -c somecommandssh somecommand

的主要限制~/.pam_environment是你只能在那里放置简单的赋值,而不是复杂的 shell 语法。该文件的语法如下。

  • 文件被逐行解析。
  • 每行必须具有VAR=VALUEVAR 由字母、数字和下划线组成的形式。替代形式VAR DEFAULT=value允许使用${VAR}语法和特殊变量@{HOME}和来扩展环境变量@{SHELL}
  • # 开始评论,它不能出现在值中。
  • 如果 VALUE 被 包围",则 VAR 设置为引号之间的字符串。
  • \$\@插入文字$@长行可以通过用 . 转义换行符来分割\
  • 如果存在语法错误,例如没有=空格或未加引号的空格,则会从环境中删除该变量。

因此,从好的方面来说,它~/.pam_environment适用于多种情况。不利的一面是,您不能使用命令的输出(例如测试目录或程序是否存在),并且某些字符(#"、换行符)不可能或难以放入值中。

放什么 ~/.profile

此文件应具有可移植 (POSIX) sh 语法。只有使用KSH或bash扩展(阵列[[ … ]]等),如果你知道你的系统有这些炮弹为/bin/sh

该文件可以由自动化应用程序中的脚本读取,因此它不应调用产生任何输出的程序或调用exec. 如果您想在文本模式登录时执行此操作,请仅对交互式 shell 执行此操作。例子:

case $- in *i*)
  # Display a message if I have new mail
  if mail -e; then echo 'You have new mail'; fi
  # If zsh is available, and this looks like a text-mode login, run zsh
  case "`ps $PPID` " in
    *" login "*)
      if type zsh >/dev/null 2>/dev/null; then exec zsh; fi;;
  esac
esac
Run Code Online (Sandbox Code Playgroud)

这是一个/bin/sh用作登录 shell 并切换到您喜欢的 shell的示例。另请参阅当我的系统管理员拒绝让我更改它时,如何使用 bash 作为我的登录 shell

~/.profile非图形登录什么时候不读?

不同的登录 shell读取不同的文件。

如果您的登录 shell 是 bash

Bash 读取~/.bash_login或者~/.bash_profile如果它们存在而不是~/.profile. ~/.bashrc即使bash是交互式的,它也不会在登录 shell 中读取。为了永远不必再次记住这些怪癖,请~/.bash_profile使用以下两行创建一个:

. ~/.profile
case $- in *i*) . ~/.bashrc;; esac
Run Code Online (Sandbox Code Playgroud)

另请参阅使用 bash 设置环境变量时应使用哪些设置文件?

如果您的登录 shell 是 zsh

Zsh 读取~/.zprofileand ~/.zlogin,但不是~/.profile。zsh 的语法与 sh 不同,但可以~/.profile在 sh 仿真模式下读取。您可以将它用于您的~/.zprofile

emulate sh -c '. ~/.profile'
Run Code Online (Sandbox Code Playgroud)

另请参阅Zsh 未命中 ~/.profile

如果您的登录 shell 是其他 shell

除了/bin/sh用作登录外壳程序和仅用作交互式外壳程序的最喜欢的外壳程序(例如鱼)之外,您在那里无能为力。这就是我对 zsh 所做的。有关从 调用另一个 shell 的示例,请参见上文~/.profile

远程命令

在不通过交互式 shell 调用远程命令时,并非所有 shell 都会读取启动文件。

Ksh 读取由ENV变量指定的文件,如果您设法传递它。

~/.bashrc如果Bash不是交互式的(!) 并且它的父进程被调用rshd或调用,则Bash 会读取sshd。所以你可以开始你~/.bashrc

if [[ $- != *i* ]]; then
  . ~/.profile
  return
fi
Run Code Online (Sandbox Code Playgroud)

Zsh 总是~/.zshenv在启动时读取。请谨慎使用,因为 zsh 的每个实例都会读取它,即使它是您设置其他变量的子shell。如果 zsh 是您的登录 shell,并且您想使用它来设置仅用于远程命令的变量,请使用保护:在 中设置一些变量~/.profile,例如MY_ENVIRONMENT_HAS_BEEN_SET=yes,并在阅读 之前检查此保护~/.profile

if [[ -z $MY_ENVIRONMENT_HAS_BEEN_SET ]]; then emulate sh -c '~/.profile'; fi
Run Code Online (Sandbox Code Playgroud)

图形化登录案例

许多发行版、显示管理器和桌面环境都安排运行~/.profile,或者通过从启动脚本中明确获取它或者通过运行登录 shell。

不幸的是,没有通用方法来处理~/.profile未读取的发行版/DM/DE 组合。

如果您使用由 启动的传统会话~/.xsession,则应在此处设置环境变量;通过采购~/.profile(即. ~/.profile)来做到这一点。请注意,在某些设置中,桌面环境启动脚本将~/.profile再次来源。

  • @qodeninja 如果`$-` 匹配模式`*i*`,即如果`$-` 包含`i`,即如果shell 是交互式的,则执行以下指令(直到匹配的`;;` 或`esac`) . (2认同)