Set System Env-Vars in UNIX

Set System Env-Vars in UNIX

Intro

许多教程在设置 macOS 环境变量时,建议在 ~/.bashrc$ZDOTDIR/.zshrc 等文件内设置环境变量。这种方式是十分简单的,大多数情况下也没有问题。

其局限是,上述方案只是修改 Shell 的配置文件,这只对 bash/zsh 会话生效。这会带来几点不方便:

  1. 如果需要在 nushell 和 zsh 直接切换(问就是闲的),环境变量需要重复定义
  2. 部分 GUI 应用不能读取 shell 配置文件中的环境变量,例如在 $ZDOTDIR/.zshrc 中设置 $EDITOR=nvim,kitty 不能读取到这个变量。参考这个 Issue Opening preferences uses vim even though $EDITOR is set to nvim · Issue #580 · kovidgoyal/kitty

要么在每个 Shell 的配置文件中设置一遍环境变量。或者,只在 $ZDOTDIR/.zshrc 中使用 export 设置环境变量,在 zsh 中执行 exec bash 进入 bash(or any other shell),这样 $ZDOTDIR/.zshrc 中设置的环境变量也能被传递给 bash。这样多少有点丑陋,且有一定的性能开销,虽然对于现代计算机来说这点开销算不了什么

Shell 变量 & 环境变量

Shell 变量:这是一种只在当前 Shell 会话中有效的变量。它不会被该 Shell 启动的其他进程(子进程)所看到。

SHELL
 (zsh)$ x=1 (zsh)$ echo $x 1 (zsh)$ bash (bash)$ echo $x [NO-OUTPUT]

环境变量:这是一种特殊的变量,它不仅在当前 Shell 会话中有效,还会被传递给所有由该 Shell 启动的子进程;

SHELL
 (zsh)$ export x=1 (zsh)$ echo $x 1 (zsh)$ bash (bash)$ echo $x 1

Read More: export command in Linux with Examples - GeeksforGeeks

Example Case

这里以设置 XDG_* 系列变量为例进行演示。关于 XDG 规范的介绍,可查阅 XDG Base Directory - ArchWiki

Info

macOS 使用  launchd  进程来管理守护进程和代理,而你还可以用它来运行 shell 脚本。你不与  launchd  直接交互,而是使用  launchctl  命令来载入或卸载  launchd  守护进程和代理。

在系统启动期间,launchd  是内核在设置电脑时首先运行的进程。若你想要 shell 脚本作为守护进程运行,应由  launchd  来启动它。其他用于启动守护进程和代理的机制可能会被 Apple 酌情移除。

你可以通过在以下文件夹中查看配置文件来了解由  launchd  管理的各种守护进程和代理:

文件夹用途
/System/Library/LaunchDaemonsApple 提供的系统守护进程
/System/Library/LaunchAgentsApple 提供的基于每个用户且所有用户适用的代理
/Library/LaunchDaemons第三方系统守护进程
/Library/LaunchAgents基于每个用户且所有用户适用的第三方代理
~/Library/LaunchAgents仅适用于登录用户的第三方代理

步骤 1:创建环境设置脚本

首先,在任意位置创建一个 Shell 脚本,例如 ~/.local/bin/sys_envs

BASH
touch ~/.local/bin/sys_envs

脚本内容如下,针对 MacOS 它使用 launchctlsetenv 子命令来设置环境变量。

BASH
sys_envs
#! /bin/bash# Define a macro function for setting environment variablesenv() {    os_name=$(uname -s)    case "$os_name" in        Darwin)            # For macOS, use launchctl to set environment variables            launchctl setenv "$1" "$2"            # launchctl setenv does not work for current shell session(which executes this script),            # for example, when set `XDG_CONFIG_HOME/bat`, the XDG_CONFIG_HOME will be expanded as empty string.            export "$1=$2"            ;;        Linux)            export "$1=$2"            # Replace $HOME with ~ for /etc/environment            env_value="${2//$HOME/~}"            # Append to /etc/environment with sudo            echo "$1=$env_value" | sudo tee -a /etc/environment > /dev/null            ;;        *)            echo "Unsupported OS: $os_name"            exit 1            ;;    esac}env XDG_BIN_HOME "$HOME/.local/bin"env XDG_CACHE_HOME "$HOME/Library/Caches"env XDG_CONFIG_HOME "$HOME/.config"env XDG_CONFIG_DIRS "/etc/xdg"env XDG_DATA_HOME "$HOME/.local/share"env XDG_DATA_DIRS "/usr/local/share/:/usr/share/"env XDG_STATE_HOME "$HOME/.local/state"# https://wiki.archlinux.org/title/XDG_user_directoriesenv XDG_DESKTOP_DIR "$HOME/Desktop"env XDG_DOCUMENTS_DIR "$HOME/Documents"env XDG_DOWNLOAD_DIR "$HOME/Downloads"env XDG_MUSIC_DIR "$HOME/Music"env XDG_PICTURES_DIR "$HOME/Pictures"env XDG_PUBLICSHARE_DIR "$HOME/Public"env XDG_VIDEOS_DIR "$HOME/Movies"# Define paths for common programs with partial XDG support# https://wiki.archlinux.org/title/XDG_Base_Directory#Partialenv CARGO_HOME "$XDG_DATA_HOME/cargo"env FFMPEG_DATADIR "$XDG_CONFIG_HOME/ffmpeg"env LESSHISTFILE "$XDG_STATE_HOME/less_history"env MYPY_CACHE_DIR "$XDG_CACHE_HOME/mypy"env NODE_REPL_HISTORY "$XDG_STATE_HOME/node_repl_history"env PYENV_ROOT "$XDG_DATA_HOME/pyenv"env PYTHONPYCACHEPREFIX "$XDG_CACHE_HOME/python"env PYTHONUSERBASE "$XDG_DATA_HOME/python"env PYTHON_HISTORY "$XDG_STATE_HOME/python_history"env RIPGREP_CONFIG_PATH "$XDG_CONFIG_HOME/ripgrep/config"env RUSTUP_HOME "$XDG_DATA_HOME/rustup"env WORKON_HOME "$XDG_DATA_HOME/virtualenvs"# dockerenv DOCKER_CONFIG "$XDG_CONFIG_HOME/docker"env MACHINE_STORAGE_PATH "$XDG_DATA_HOME/docker_machine"# npmenv NPM_CONFIG_USERCONFIG "$XDG_CONFIG_HOME/npm/npmrc"# zshenv ZDOTDIR "$XDG_CONFIG_HOME/zsh"env ZSH_PROFILE "$XDG_CONFIG_HOME/zsh/profile"env HISTFILE "~/.cache/zshhistory"# yazienv YAZI_CONFIG_HOME "$XDG_CONFIG_HOME/yazi"

NuShell Rewrite-Version

前面 Bash 版本的脚本是写入到/etc/environment中,只支持普通键值对。而/etc/profile.d/*.sh为 shell script,可以在 value 中引用变量,相对灵活一点。这里给出一个 nushell 版本的脚本,将 Linux 环境变量写入到/etc/profile.d/gnix-env.sh

Note

针对个人跨平台需求,这里把环境变量的设置封装成一个 env 函数中,对于 Arch Linux 来说,系统级环境变量可以写入 /etc/environment/etc/profile.d/*.sh

如需针对单用户设置,可以考虑写入~/.pam_environment,详见pam_env.conf(5) - Linux man page

NU
sys_envs.nu
#! /usr/bin/env nu# Define a function to set environment variables cross-platformdef --env env [name: string value: string] {  # Set env var for current session  load-env {$name: $value}  match $nu.os-info.name {    "macos" => {      launchctl setenv $name $value    }    "linux" => {      let env_file = "/etc/profile.d/gnix-env.sh"      touch $env_file      let lines = (open $env_file | lines | where not ($it | str starts-with $"export ($name)="))      let env_value = ($value | str replace $nu.home-path "$HOME")      let updated = ($lines | append $"export ($name)=\"($env_value)\"")      $updated | str join (char nl) | save --force $env_file    }    "windows" => {      setx $name $value    }  }}

Step 2. Load the script at Login/StartUp

  1. 如果 ~/Library/LaunchAgents 目录不存在,则创建它:mkdir -p ~/Library/LaunchAgents
  2. 创建并编辑 ~/Library/LaunchAgents/env.plist 文件,写入以下内容。
XML
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0">  <dict>    <key>Label</key>    <string>com.user.environment-vars</string>    <key>ProgramArguments</key>    <array>      <!-- 这里需要替换成你自己的脚本路径 -->      <string>/Users/gjx/.local/bin/sys_envs</string>    </array>    <key>RunAtLoad</key>    <true />    <key>KeepAlive</key>    <false />  </dict></plist>

完成以上步骤后,下次登录时,launchd 就会自动执行该脚本,从而设置好所有环境变量。

Limitations

Apple 不保证 launchd 加载服务的确切顺序,也就是可能先重启了 Terminal,而后env.plist 才被加载,这会导致 Terminal 无法获取到新的环境变量。

解决方法如下:

  • 重新登录:当前用户先 Log Out(快捷键 ⌘ + ⇧ + Q),再重新 Login
  • 手动重启应用:字面意思
  • 禁用会话恢复:在系统设置中,关闭“Reopen windows when logging back”的功能。

另外就是无法通过 launchctl 修改 PATH 环境变量,想在 System-Wide 层级设置 PATH,需要在 /etc/paths 中修改。

Ref

Set System Env-Vars in UNIX

https://vluv.space/Unix环境变量/

作者

GnixAij

发布于

2025-07-27

更新于

2025-09-06

许可协议

评论