设置环境变量的最好的发行版/与外壳无关的方法是什么?


31

问题说明了一切。我目前使用Arch Linux和zsh,但是我想要一个(至少)可以在VT和xterm上运行的解决方案,并且(希望,最好是)如果我切换发行版或shell,它也可以继续工作。

我已经在不同发行版的文档中听到了对该问题的截然不同的答案。Ubuntu说“使用.pam_environment”。我认为在Arch中,他们的建议取决于您的外壳。当前,我将所有内容都放在.profile中,如果外壳由于某种原因(例如bash如果存在.bash_profile存在)不提供该资源,则可以通过手动采购来覆盖它。但是似乎必须有一个更好的方法。


2
这与发行版无关,与shell无关。不过,不确定是否有可移植的方法。
Joseph R.

真棒
奋斗

Answers:


29

不幸的是,没有完全可移植的位置来设置环境变量。最接近的两个文件是~/.profile,这是传统位置,在许多设置中都是开箱即用的,以及~/.pam_environment是一种现代,普通但有限的选择。

放入什么 ~/.pam_environment

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

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

主要的限制~/.pam_environment是您只能在其中放置简单的分配,而不能在其中放置复杂的Shell语法。该文件的语法如下。

  • 文件逐行解析。
  • 前导空格被忽略。
  • 您可以选择以export和一个空格作为开始行(不是制表符,请转到图)。
  • 之后,每一行必须采用VAR=VALUEVAR由字母,数字和下划线组成的形式。
  • # 开始注释,它不能出现在值中。
  • 如果VALUE以'或开头"并包含另一个相同的引号,则将VAR设置为引号之间的字符串(忽略第二个引号之后的所有内容)。否则,将VAR设置为=符号后的字符串。
  • 如果没有=,则将变量从环境中删除。

因此,从好的方面来说,它~/.pam_environment可以在多种情况下工作。不利的一面是,您无法进行任何动态设置,例如,将变量的值基于另一个变量(例如,将目录添加到PATH)或使用命令的输出(例如,测试是否存在目录或程序),以及一些字符(#'",换行符)不可能或很难输入值。

放入什么 ~/.profile

该文件应具有可移植(POSIX)sh语法。仅[[ … ]]当您知道系统将这些shell作为时,才使用ksh或bash扩展名(数组等)/bin/sh

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

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

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

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

不同的登录外壳读取不同的文件。

如果您的登录shell是bash

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

. ~/.profile
case $- in *i*) . ~/.bashrc;; esac

另请参见应使用哪些设置文件来通过bash设置环境变量?

如果您的登录外壳是zsh

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

emulate sh -c '. ~/.profile'

另请参见Zsh未达到〜/ .profile

如果您的登录外壳是其他外壳

您只能在其中做很多事情,只需将其/bin/sh用作登录外壳程序并将您喜欢的外壳程序(例如fish)仅用作交互式外壳程序即可。那就是我对zsh所做的。请参阅上面的示例,从中调用另一个shell ~/.profile

远程命令

当不通过交互式外壳程序而调用远程命令时,并非所有外壳程序都读取启动文件。

ENV如果您设法通过Ksh ,则将读取变量指定的文件。

Bash会读取~/.bashrc它是否不是交互式(!)并且其父进程称为rshdsshd。所以,你就可以开始你~/.bashrc

if [[ $- != *i* ]]; then
  . ~/.profile
  return
fi

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

图形登录的情况

许多发行版,显示管理器和桌面环境都安排运行~/.profile,可以通过从启动脚本中明确采购来运行它,也可以通过运行登录外壳程序来运行。

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

如果使用以开头的传统会话~/.xsession,则应在此处设置环境变量。通过获取这样做~/.profile(即. ~/.profile)。请注意,在某些设置中,桌面环境启动脚本将~/.profile再次提供。


怎么case $- in *i*)办?
qodeninja 2014年

2
@qodeninja 如果匹配模式,则执行以下指令(直到匹配;;esac),即如果包含,即外壳是交互式的。$-*i*$-i
吉尔斯

$-是当前设置的外壳程序选项的字符串。(如set -x)。 i表示交互式外壳。
彼得·科德斯

~/.config/env即使不进行仿真,您是否也只能提供通用文件?
凯文·索特尔

1
@StéphaneChazelas这是一个纯粹的观点。我保持.profile使用相当旧的Bourne外壳的兼容性,但我知道有些人不在乎。我不反对假设sh = bash用于他们自己的文件的人,我只关心他们是否发布#!/bin/sh使用bash功能的脚本。
吉尔(Gilles)'所以

4

据我所知,还没有发行版和外壳不可知的标准来设置环境变量。

最常见的事实标准似乎是/etc/profileand ~/.profile。第二个最常见的似乎是/etc/environment~/.pam_environment

在我看来,我找到的所有文档也都已经找到。无论如何,我在这里列出它们以供其他读者阅读。

  • Debian建议使用/etc/profile~/.profile链接)。
  • Ubuntu建议使用/etc/environment~/.pam_environmentlink)。
  • Arch Linux提到了/etc/profile/etc/environmentlink)。

奖励:质疑/etc/environmentdebian中使用和/或滥用的文字(链接,最新更新2008)。


无论使用什么文件,您仍然会遇到不同外壳之间不兼容的语法。
Joseph R.

1
@JosephR。大多数shell都不会保持向后兼容sh吗?只要您坚持使用POSIX,我就会以为您会没事的……
evilsoup

1
AFAIK,您不能csh以POSIX方式在变量中和朋友之间分配变量(您需要类似set或的方法setenv
Joseph R.

0

我添加了以下脚本〜/ bin / agnostic_setenv:

#!/bin/csh -f
set args = ($*)
if ($#args == 1) then
   echo "export $args[1]="
   exit 0
endif

if ($#args == 2) then
   if ("$args[1]" =~ *csh*) then 
      echo "setenv $args[2]"
      exit 0
   else
      echo "export $args[1]=$args[2]"
      exit 0
   endif
endif

echo "setenv $args[2] $args[3]"

在〜/ .perl-homedir中,我使用:

eval `${HOME}/bin/agnostic_setenv $shell PERL_HOMEDIR 0`

agnostic_unsetenv的类似脚本:

#!/bin/csh -f
set args = ($*)
if ($#args == 1) then
   echo "export $args[1]"
   exit 0
endif

echo "unsetenv $args[2]"
exit 0
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.