env 和 printenv 顯示哪些環境變數/shell 變數
我已經看到了諸如此類的相關問題,但他們沒有為我的問題提供確切的答案
從我的實驗以及這個答案來看,
printenv
幾乎env
都顯示了相同的系統變數集。如果我將變數設置為
- /etc/bash.bashrc(應該是系統範圍的系統變數)
SYSTEM_ENVI=1000
- ~/.bashrc (應該是使用者特定的系統變數)
USER_ENVI=10
我什至註銷並登錄,因此 /etc/environment 生效。發生以下情況:
$echo $SYSTEM_ENVI //outputs 1000 $echo $USER_ENVI //outputs 10 $CURR_ENVI=1 $env | grep USER_ENVI //nothing shows up, the same if I grepped SYSTEM_ENVI or CURR_ENVI $set | grep USER_ENVI //shows up USER_ENVI assignment, the same if I grepped SYSTEM_ENVI or CURR_ENVI
我的問題是:
- 什麼系統變數做
printenv
/env
列印?- 應該使用
set
查看所有可訪問變數(系統變數和局部變數)而不是printenv
orenv
嗎?關於不重複的理由
就我而言,這個問題和明顯的答案幫助我認識到以下事實:
- Shell 變數不是環境變數
- /etc/bash.bashrc或*~/.bashrc中的賦值不會創建環境變數,而是指示互動式非登錄 shell 程序在啟動時創建和初始化這些shell 變數*。
我認為我的問題不一定與這個問題不同,但是閱讀那個問題的標記答案並不像這篇文章中給出的答案那樣讓我滿意。
env
並printenv
列印由執行它們的命令提供給它們的環境字元串列表(意味著包含環境變數定義)。呼叫者最終將執行以下操作:execve("/usr/bin/env", argv, envp);
系統呼叫 where
argv
和envp
是兩個字元串列表。
env
/printenv
只列印 中的字元串列表envp
,每行一個。按照慣例,其中的字元串
envp
採用 formatvar=value
,但它們不一定是(我不知道有任何execve()
實現它的實現),並且大多數env
實現printenv
並不關心它們何時顯示它們。當呼叫者是 POSIX shell 時,它將包含在
envp
它傳遞給標記為導出env
的 shell 變數列表中(因為使用者在其上呼叫/ ,或者因為變數已經在 shell 的環境中)啟動時收到)。export``typeset -x
如果 shell 在啟動時收到的某些環境變數無法映射到 shell 變數,或者如果
envp
它收到的任何字元串不包含=
字元,則取決於 shell 實現,這些字元串將被傳遞原封不動,否則外殼會剝去它們或其中的一些。例如
bash
,使用 GNUenv
傳遞任意變數名稱的列表(env
雖然不能傳遞任意 envp 字元串,但它們必須包含 a=
,並且使用的那些setenv()
不能傳遞以=
¹ 開頭的一些)。$ env -i '=foo' '1=x' '+=y' bash -c printenv +=y 1=x [...]
(具有空名稱的變數已刪除,但其他變數未刪除)。
此外,如果 shell 接收
envp
到同一個變數名的多個字元串,取決於 shell,它們將全部傳遞,或者只傳遞第一個,或者只傳遞最後一個。
set
in POSIX shells 列印 shell 變數列表,包括支持數組/雜湊類型的 shell 的非標量變數,無論它們是否已標記為導出。在 POSIX shell 中,您還可以使用
export -p
列出已標記為導出的變數。與env
/相反printenv
,它還列出了已標記為導出但尚未被賦予任何值的變數。在類似 Korn 的 shell
ksh
中,如zsh
或bash
,您還可以使用typeset
來獲取更多資訊,包括變數的屬性,以及按類型列出變數(如typeset -a
列出數組變數)。在這裡,通過添加
USER_ENVI=10
您的~/.bashrc
,您正在配置 shell 的互動式非登錄呼叫,以在啟動時bash
定義一個USER_ENVI
shell變數。由於您沒有使用export
,因此該變數仍然是一個 shell 變數(除非它在bash
啟動時位於環境中),因此它不會作為環境變數傳遞給該 shell 執行的命令。
/etc/environment
,本身,在 Ubuntu 16.04 上由pam_env.so
可插入的身份驗證模組讀取。login
像,sshd
,等登錄的應用程序lightdm
將讀取這些文件(如果配置為使用pam_env.so
)/etc/pam.d
並將相應的環境變數(此處與 shell 變數無關)傳遞給它們在您進行身份驗證後以您的名字開頭的命令(例如您的登錄 shell 用於login
/sshd
,或您的圖形會話管理器lightdm
…)。由於預設情況下會繼承環境,因此當您的會話管理器執行終端仿真器進而執行您的登錄 shell 時,這些環境變數將在每一步傳遞,您的 shell 會將它們映射到您可以在命令中擴展的 shell 變數符合之類的東西
echo "$VAR"
。
pam_env
env 文件/etc/environment
看起來像 shell 腳本,但pam_env
不呼叫 shell 來解析它們,並且只理解 shell 語法的一個子集,並且只允許定義名稱由一個或多個 ASCII 字母數字字元或下劃線組成的變數(它確實讓您定義一個123
不是有效的 POSIX shell 變數名的變數)。¹,要傳遞任意 env 字元串列表,您也可以
execve()
直接呼叫:perl -e 'require "syscall.ph"; $cmd = "/bin/zsh"; $args = pack("p*x[p]", "sh", "-c", "printenv"); $env = pack("p*x[p]", "a=b", "a=c", "", "+=+", "=foo", "bar"); syscall(SYS_execve(), $cmd, $args, $env)'
在這裡測試
zsh
而不是bash