Shell 變量的作用域(Scope),就是 Shell 變量的有效范圍(可以使用的范圍)。
Shell 變量的作用域可以分為三種:
- 有的變量只能在函數內部使用,這叫做局部變量(local variable);
- 有的變量可以在當前 Shell 進程中使用,這叫做全局變量(global variable);
- 有的變量可以在子進程中使用,這叫做環境變量(environment variable)。
Shell 局部變量
原理 | 代碼 | 輸出結果 | 結論 | 其他 |
---|---|---|---|---|
Shell 支持自定義函數,但是 Shell 函數和其他編程語言函數的一個不同點就是:在 Shell 函數中定義的變量默認也是全局變量,它和在函數外部定義變量擁有一樣的效果。 |
|
99 | a 是在函數內部定義的,但是在函數外部也可以得到它的值,證明它的作用域是全局的,而不是僅限於函數內部。 | |
要想變量的作用域僅限於函數內部,可以在定義時加上local 命令,此時該變量就成了局部變量。 |
|
輸出結果為空 | 輸出結果為空,表明變量 a 在函數外部無效,是一個局部變量。 | Shell 變量的這個特性和 JavaScript 中的變量是類似的。在 JavaScript 函數內部定義的變量,默認也是全局變量,只有加上var 關鍵字,它才會變成局部變量。 |
Shell 全局變量
在 Shell 中定義的變量,默認就是全局變量,所謂全局變量,就是指變量在當前的整個 Shell 進程中都有效。
需要強調的是,全局變量的作用范圍是當前的 Shell 進程,而不僅僅是當前的 Shell 腳本文件,它們是不同的概念。打開一個 Shell 窗口就創建了一個 Shell 進程,打開多個 Shell 窗口就創建了多個 Shell 進程,每個 Shell 進程都是獨立的,擁有不同的進程 ID。在一個 Shell 進程中可以使用 source 命令執行多個 Shell 腳本文件,此時全局變量在這些腳本文件中都有效。
關注點 | 操作 | 對比1 | 對比2 | 其他 |
---|---|---|---|---|
全局變量的作用范圍是當前shell進程 | 打開兩個shell窗口,驗證變量作用域 |
打開一個 Shell 窗口,定義一個變量 b 並賦值為 88,然后打印,這時在同一個 Shell 窗口中是可正確打印變量 b 的值的。 |
再打開一個新的 Shell 窗口,同樣打印變量 b 的值,但結果卻為空。 |
|
全局變量的作用范圍是不僅僅在當前的 Shell 腳本文件 | 在當前shell窗口定義變量a,可以被腳本1使用,在腳本1中定義的變量b,可以在腳本2中使用 | a.sh
|
b.sh
|
這三條命令都是在一個進程中執行的,從輸出結果可以發現,在 Shell 窗口中以命令行的形式定義的變量 a,在 a.sh 中有效;在 a.sh 中定義的變量 b,在 b.sh 中也有效,變量 b 的作用范圍已經超越了 a.sh。 |
在Shell 窗口,輸入以下命令 a=99 . ./a.sh -->99 |
在同樣的shell中執行. ./b.sh -->200 |
shell全局變量的易錯點:
腳本 | 分析 |
---|---|
PARAM_NUMBER=0; cat "/home/roaddb/test/55.txt" | while read line do let PARAM_NUMBER=${PARAM_NUMBER}+1; done echo "${PARAM_NUMBER}" |
結果PARAM_NUMBER的值還是0,原因是在進行 cat的過程中, 相當於打開了一個新的shell,變量不在作用范圍。 |
#!/bin/bash PARAM_NUMBER=0; while read line do let PARAM_NUMBER=${PARAM_NUMBER}+1; done<"/home/roaddb/test/55.txt" echo "${PARAM_NUMBER}" |
PARAM_NUMBER的值是正確的 |
linux shell中./a.sh , sh a.sh , source a.sh, . ./a.sh的區別
腳本 | 知識點 | |
---|---|---|
#! /bin/bash echo "PID of this script: $$" echo "PPID of this script: $PPID" |
echo $$ 輸出當前進程號-------->590
echo $PPID 輸出父進程號-------->589
|
|
執行方式 | 結果 | |
a.sh | 報錯bash: a.sh: command not found |
|
./a.sh |
bash: ./a.sh: Permission denied 需要權限,設置權限為777后執行 PID of this script: 92669 PPID of this script: 590 |
啟了一個子shell來執行a.sh,所以可以看到PPID of this script: 590 |
sh ./a.sh | PID of this script: 92878 PPID of this script: 590 |
|
sh a.sh | PID of this script: 92928 PPID of this script: 590 |
|
source ./a.sh | PID of this script: 590 PPID of this script: 589 |
在當前shell中執行腳本 |
source a.sh | PID of this script: 590 PPID of this script: 589 |
|
. ./a.sh |
PID of this script: 590 PPID of this script: 589 |
|
../a.sh | 報錯bash: ../a.sh: No such file or directory |
最后要說明的兩點是:
1. 用sh和source去執行時, 不要求a.sh有可執行權限, 但單獨./a.sh這樣去搞時,需要可執行權限
2. 大家在開發項目時,經常需要設置環境變量, 當然是用source啊, 確保在當前shell生效
Shell 環境變量
全局變量只在當前 Shell 進程中有效,對其它 Shell 進程和子進程都無效。如果使用export
命令將全局變量導出,那么它就在所有的子進程中也有效了,這稱為“環境變量”。
環境變量被創建時所處的 Shell 進程稱為父進程,如果在父進程中再創建一個新的進程來執行 Shell 命令,那么這個新的進程被稱作 Shell 子進程。當 Shell 子進程產生時,它會繼承父進程的環境變量為自己所用,所以說環境變量可從父進程傳給子進程。不難理解,環境變量還可以傳遞給孫進程。
注意,兩個沒有父子關系的 Shell 進程是不能傳遞環境變量的,並且環境變量只能向下傳遞而不能向上傳遞,即“傳子不傳父”。
使用env可以查看所有環境變量!!!
創建 Shell 子進程最簡單的方式是運行 bash 命令,如圖 2 所示。
圖2:進入 Shell 子進程
通過exit
命令可以一層一層地退出 Shell。
下面演示一下環境變量的使用:
[c.biancheng.net]$ a=22 #定義一個全局變量 [c.biancheng.net]$ echo $a #在當前Shell中輸出a,成功 22 [c.biancheng.net]$ bash #進入Shell子進程 [c.biancheng.net]$ echo $a #在子進程中輸出a,失敗 [c.biancheng.net]$ exit #退出Shell子進程,返回上一級Shell exit [c.biancheng.net]$ export a #將a導出為環境變量 [c.biancheng.net]$ bash #重新進入Shell子進程 [c.biancheng.net]$ echo $a #在子進程中再次輸出a,成功 22 [c.biancheng.net]$ exit #退出Shell子進程 exit [c.biancheng.net]$ exit #退出父進程,結束整個Shell會話
可以發現,默認情況下,a 在 Shell 子進程中是無效的;使用 export 將 a 導出為環境變量后,在子進程中就可以使用了。export a
這種形式是在定義變量 a 以后再將它導出為環境變量,如果想在定義的同時導出為環境變量,可以寫作export a=22
。
我們一直強調的是環境變量在 Shell 子進程中有效,並沒有說它在所有的 Shell 進程中都有效;如果你通過終端創建了一個新的 Shell 窗口,那它就不是當前 Shell 的子進程,環境變量對這個新的 Shell 進程仍然是無效的。請看下圖:第一個窗口中的環境變量 a 在第二個窗口中就無效。
環境變量也是臨時的,通過 export 導出的環境變量只對當前 Shell 進程以及所有的子進程有效,如果最頂層的父進程被關閉了,那么環境變量也就隨之消失了,其它的進程也就無法使用了,所以說環境變量也是臨時的。
shell配置文件
那如何讓一個變量在所有 Shell 進程中都有效,不管它們之間是否存在父子關系呢?只有將變量寫入 Shell 配置文件中才能達到這個目的!Shell 進程每次啟動時都會執行配置文件中的代碼做一些初始化工作,如果將變量放在配置文件中,那么每次啟動進程都會定義這個變量。修改配置文件可參考《Shell配置文件的加載》《編寫自己的Shell配置文件》。