標籤:

用python去修改linux環境變數為何無效?

我用python寫了一個安裝腳本,安裝完了後想永久配置環境,我選擇修改/etc/profile,然後修改完了後執行os.system("source /etc/profile"),發現並沒有起作用。

修改/etc/profile 和執行source都是寫在python腳本里的,請問有什麼辦法能讓source指令真正生效。

def config_sys_path(path):
if (check_is_config_path(path)):
print path + " has already been config"
return
f = codecs.open(env_file_path, "a")
env_path = "export PATH=$PATH:" + path
f.write("
" + env_path)
if (path.find("GraphicsMagick") &>= 0):
cpu_num = cpu_count()
f.write("
export OMP_NUM_THREADS=" + str(cpu_num))
f.close()
run_shell("source " + env_file_path)

====================問題補充====================

非常感謝 張成和黃哥的解答,張成哥的耐心解答我很感動,從中學到了一些重要的知識點。

說一下我想做什麼吧,是這樣的,有一種切圖工具(GraphicsMagick),安裝的時候需要依賴一些別的安裝包,挨個安裝有點麻煩,所以我用python寫了一個簡單的安裝配置的腳本。

全代碼見:

https://github.com/linxiaobai/GraphicsMagickInstallShell/blob/master/installUtil.py

在安裝libjepg-turbo的時候,需要用到nasm,但是由於沒有nasm環境變數,在執行configure libjpeg時會出錯,所以我想在安裝完nasm後,在腳本里執行修改環境變數,然後在安裝其他程序。

張成說的對,我無法修改所有已經在運行的程序的環境變數,所以我想當然的做法應該行不通。

解決辦法:

我想不到一個好的辦法,我只能手動source /etc/profile讓其生效後再執行安裝腳本。


提問者可能沒有正確的理解「環境變數」的含義,所以我先簡單解釋一下環境變數。

環境變數可以看作是進程的一些元信息,脫離進程談環境變數是沒有意義的。在創建進程時,可以給新創建的進程設置一些環境變數,而進程一旦運行起來之後,就沒有常規的手段可以從外部修改這個進程的環境變數了。一個進程的環境變數,可以通過以下命令查看(文件中的內容用 分割,tr 命令將 轉化為換行,這樣看得清楚):

cat /proc/$pid/environ | tr "" "
"

Bash中新創建進程時,默認會將自己的環境變數、以及export的變數都設置為子進程的環境變數。因此,如果在一個Bash中執行:

export FOO=bar

那麼這個Bash後續啟動的新進程就都有FOO這個環境變數了。

Bash如果以交互形式啟動,或者以login shell的方式啟動,啟動時會執行/etc/profile中的命令。如果/etc/profile中export了一些變數,那麼這個Bash後續創建的新進程就都有這些環境變數了。

回到題主的問題。首先,「永久配置環境」,這句話本身是一個錯誤的說法。因為:

1、你無法修改所有已經在運行的程序的環境變數。

2、Linux下沒有常規的方法可以使得所有新創建的進程都有一個預配置的環境變數。通常情況下,一個進程的環境變數由其父進程設置。如果父進程創建子進程時沒有特別處理,一般子進程會直接繼承父進程的環境變數。因此,如果要新創建的進程有某個特定的環境變數,那麼這個變數一定是被父進程或者某個祖先進程設置的。

3、/etc/profile 只有Bash以及一些其他shell會讀取,所以如果一個進程(以及其所有祖先進程)不是被bash啟動的話,那麼/etc/profile對他沒有任何影響。(當然也有一些特例,有些非shell程序也會讀取/etc/profile。)

下面解釋一下,當你向/etc/profile中寫入了export PATH和OMP_NUM_THREADS之後會有哪些效果:

  • 當你啟動一個新的Bash進程時,這個Bash進程就會有這兩個環境變數,並且它後續啟動的進程也會有這兩個環境變數。

  • 當你重啟系統後,新開Bash進程,同上。
  • 重啟系統後,由systemd啟動的進程沒有這兩個環境變數。如果是其他init系統,則看情況了,有些init系統會讀取/etc/profile,有些不會。
  • 當前python進程的環境變數沒有變化,當前python進程後續啟動的子進程也沒有這兩個環境變數,當前python進程的父進程(以及所有祖先進程)的環境變數也沒有變化。

以上是對標題 「用python去修改linux環境變數為何無效?」 的回答。實際上,這個問題也同樣是一個錯誤,或者說不完整的問題。因此,請不要追問 「如何才能生效」 的問題。如果題主理解了前面的回答,可以考慮一下:你期望哪些進程有PATH和OMP_NUM_THREADS這個環境變數?這些進程分別是怎樣啟動的(把所有祖先進程列出來)?如果某個進程沒有這個環境變數,我們在針對具體情況再去討論如何解決這個具體的問題。


環境變數是進程環境的屬性之一,當你用os.system去執行的時候,實際上是:

1 fork一個子進程

2 子進程exec另一個shell程序,執行你要執行的命令,比如你source,實際上是exec一個shell進程再source,所以子進程的環境變數被你改了

但是你這個python進程不受任何影響

要修改當前進程的環境變數,用os.putenv,或操作os.environ這個字典(實際上並不是dict的直接實例,而是一個用法和dict基本一樣的對象),這倆的區別參考os模塊的文檔


答案在這裡

source is not an executable command, it"s a shell builtin.

The most usual case for using source is to run a shell script that changes the environment and to retain that environment in the current shell. That"s exactly how virtualenv works to modify the default python environment.

Creating a sub-process and using source in the subprocess probably won"t do anything useful, it won"t modify the environment of the parent process, none of the side-effects of using the sourced script will take place.

Python has an analogous command, execfile, which runs the specified file using the current python global namespace (or another, if you supply one), that you could use in a similar way as the bash command source.

用 execfile


推薦閱讀:

編程初學者如何在GitHub尋找適合自己的小項目?
用腳本構建的程序是怎麼保持後期重構的健壯性的?
centos在python3環境下安裝PIP的問題?
python3.x 如何從str中提取bytes?
python安裝pandas包快要崩潰了?

TAG:Python | Linux |