Shell 全局及區域變數、特殊變數及環境變數 | Shell 啟動順序 | Array 變數

Shell 全局及區域變數、特殊變數及環境變數 | Shell 啟動順序 | Array 變數

OverView of Content

閱讀本文章,將帶您深入了解 Shell 腳本編程的各個方面,包括 Shell 字面常數、引號使用技巧、環境變量概述、Shell 特殊參數及更多不同變量的變數,以及 Shell 的啟動與環境變量設置等等

Linux 中有許多地方可以設定環境變量(environment variable),而它設定在哪也會影響到如何使用

寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀


Shell 字面常數、引號

在 Shell 中,不同「引號」代表的意義不同,Shell 對於命令中的引號分析如下:

A. 在執行之前 Shell 會尋找其中的變數、萬用字元、其他代數,並將其替代(至於變數部分又會與「單」、「雙」引號有關)

B. 替換完畢後,會將結果傳遞給命令

單引號:不會替換

● 要建立常數最簡單的方法(或是說標準的做法)就是使用單引號,在 單引號內的字元 不會被替換,它們將以常數的方式表現


# $ 不會被替換
echo 'Good morning~ $PLACE_HOLDER'

# * 不會被轉換
echo 'Good morning~ *'

# $() 不會啟動一個新 Shell
echo 'Good morning~ $(echo Apple)'

從下圖中,我們可以看到被單引號包裹的命令($PLACE_HOLDER)是不會執行的

被單引號括著的就是一個參數,如果要輸出單引號要使用跳脫字元(\

有時候我們會碰到需要使用單引號 ' 的時候,這時就要注意,可以使用跳過字元(反斜線 \


# 這句其實輸出了三個字面參數
# 1. 'I'
# 2. \' (跳脫字元)
# 3. 'm Alien![](https://hackmd.io/_uploads/B1dmAq4on.png)


echo 'I''\'m Aliem'

雙引號:替換、執行

● 雙引號與單引號最大的差別在,雙引號內的 特殊字元會被替換、執行


# 定義 NAME 變數
NAME=Alien

# 使用單引號呼叫變呼叫數
echo 'What up $NAME'

# 使用雙引號呼叫變呼叫數
echo "What up $NAME"

如下圖所示,變數 NAME 確實會在執行時被替換


Shell 環境變量

bash shell 用一個叫作 環境變量 (environment variable) 的特性來存儲有關 shell 和工作環境的信息

變量儲存在記憶體(內存)中

Bash shell 中,環境變量主要分為兩類:1. 局部變量2. 全局變量

讀取局部變量:set

● 局部變量只會讓創建變量的 Shell 本身訪問(其他的 Shell,包括 sub shell 也不可訪問);如果要查看全部的局部變量,可以執行以下命令

命令說明
set列出所有全局、局部變量 (並且會對變量進行排序)

## 列出所有全局、局部變量

set

局部變量一般來說都用小寫,全局變量則使用大寫

設定/移除局部變量:unset

設定局部變量:簡單使用,變量名= 變量,就可以設定局部變量


echo $my_local_variable

## 設定局部變量
my_local_variable="Hello local variable."

echo $my_local_variable

## 查看局部變量
set | gerp my_local

設定 Shell 變量時的注意事項

A. 變量最好使用單(或雙)引號包裹起來,不然 空格會被誤認變量已設定完成

錯誤範例:my_local_variable=Hello local variable.

B. 賦予值時 = 之間不要使用空格空開,否則 Shell 會解讀為單獨命令

錯誤範例:my_local_variable = "Hello local variable."

在子進程中無法獲取父進程的局部變量;不過如果使用 進程列表 的方式創建 sub shell 就可以獲取父進程局部變量


echo $my_local_variable

## 創建 sub shell
bash

## 查看進程
ps --forest

## sub shell 不可訪問 parent shell 的局部變量
echo $my_local_variable

## 退出 sub shell
exit

## 回在 parent shell 就可以訪問
echo $my_local_variable

移除局部變量:使用 unset 命令就可以移除局部變量;移除時不需使用 $ 符號


echo $my_local_variable

## 移除局部變量
unset my_local_variable

echo $my_local_variable

## 查看局部變量
set | gerp my_local

全局變量:env/printenv

● 全局環境變量就是所有 Shell 會話都可以訪問的變量;查看全局變量的命令如下

命令說明
env列出所有全局變量
printenv [指定變量] [指定變量] ...同上,不過它可以指定變量

## 只列出前 10 項全局變量
env | head

## 列出指定變量
printenv SHELL

## 可指定多個
printenv SHELL HOME

設定/移除全局變量:export

創建全局變量:使用 export 命令就可以創建一個全局變量 (變量創建方式與設定局部變量相同)

透過 export 創建的變量就可以讓 sub shell 訪問到

sub shell 取得的是原本變量的參照

不是~ sub shell 取得的是一個 變量副本,sub shell 可以改變變量的值,但是無法影響到外部變量(不會影響到原本的變量)


## 創建全局變量
export my_export_variable="Hello export variable."

echo $my_export_variable

## create sub shell
bash

## 讀取外部全局變量
echo $my_export_variable

## 修改變量副本
my_export_variable="Fix!"

## 讀取修改後的副本內容
echo $my_export_variable

## exit sub shell
exit 

## 讀取外部變量(不受內部修改影響)
echo $my_export_variable

● 刪除全局變量,同樣使用 unset 命令


unset my_export_variable

Shell 特殊參數

bash 中附有幾個「特殊參數」,可以透過這些特殊參數來達到你要的功能

Shell 腳本讀取參數:$

我們可以對腳本傳入需要的參數,像是如下


# add 是腳本
# 10、20 就是參數

add 10 20

● Shell 會將輸入參數稱為 位置参數 (positional parameter),其參數有部分限制、特性:

參數順序以 0 開始計算

第一個參數 $0 一定是腳本本身的名稱

● 參數取用方式 $參數順序當參數數量超過 9 時,必須要花括號來包裹 ${參數順序}

e.g:

第 3 個參數 -> $2

第 12 個參數 -> $11

● 以下透過讓使用者輸入一個參數,實現階乘 factorial 的運算


#!/bin/bash

factorial=1
userSet=$1

for (( a = 1; a <= userSet; a++ )) ; do
    (( factorial *= a ))
    echo "$a: $factorial"
done

echo "The factorial of $userSet is $factorial."

● 如果你要輸入更多的參數,請使用 空格 區分不同參數

● 如果輸入的參數有包含空格,需使用單引號(雙引號也可以)包裹空格

● 使用參數時,建議使用 test 命令(-n-z)來測試參數是否存在,避免取不到參數的錯誤

Shell 腳本取得參數總數:$#

$# 符號可以取得使用者輸入的參數總數,這個參數總數不包括腳本本身,也就是不包括 $0

符號特殊改變

有個特殊情況,如果外部已經用「$ + 花括號, ${ }」包裹,那取參數總數的方式就會變成 ${!#}花括號內不能用 $,要改用 !

使用 ${!#} 時,如果參數總數為 0,則輸出腳本自身

● 取得輸入給 Shell 腳本參數總數的範例:


#!/bin/bash

tpc=$#

echo "total param count = $tpc"

echo "total param count = ${!#}"

if [ $tpc -lt 2 ] ; then
    echo "param too less"
elif [ $tpc -gt 2 ] ; then 
    echo "param too more"
else 
    echo "result: $[ $1 + $2 ]"
fi

Shell 腳本取得所有參數:$*$@ 差異

$*$@ 兩個符號都可以取得所有參數,不過他們兩個還是有不同的地方;差異點在於對於看待參數時是否分離

符號特點
$*將所有參數視為一個個體(整體)
$@將所有參數分開看待

● Shell 讀取所有參數的範例:


#!/bin/bash

for tmp in "$*" ; do 
    echo "\$*: $tmp"
done

for tmp in "$@" ; do
    echo "\$@: $tmp"
done

從下圖中,我們可以看到 $* 確實將全部的參數視作一個整體

取得上次命令退出碼:$?

● 使用 $? 可以取得上次執行的命令的退出碼(這邊要特別注意的是,$? 符號只保存最近一次,它不會保存超過一次以上的退出碼),而取出上次命令的退出碼數值如果是 0 則代表上一次的命令執行成功,而 非 0 則代表上一次的命令失敗;範例如下…


echo "HelloWorld"

echo $?

ls 123

echo $?

自身 Shell PID:$$

使用 $$ 符號,就可以獲取當前使用的 Shell PID;範例如下…


echo $$

Shell 更多不同變量的變量

除了上述的基礎定義 Shell 變量的方法外,Shell 也可以定義 array 變量、默認變量、PATH 變量... 等等

Shell 數組變量:Array 變量

● 數組(Array)變量在賦予值時,是放置在括號內,並使用空格區分開;格式說明如下表

功能格式說明e.g
宣告 Array放置在括號 () 內,並使用空格 () 區分開MyArray=(A B C D E F G)
單個值數組變數放置花括號 {} 內,並使用中括號 [] 指定${MyArray[2]}
全部值同上,不過中括號內使用 *將全部數據視為一個數據${MyArray[*]}
全部值同上,不過中括號內使用 @它會將數組中的數據 單獨 展開${MyArray[@]}
取消變數使用 unset-

## 宣告並設定值
MyArray=(A B C D E F G)

## 預設取出第一個
echo $MyArray

## 取單個值
echo ${MyArray[2]}

## 取全部值
echo ${MyArray[*]}

## 取消變數
unset MyArray

echo ${MyArray[*]}

● 另外一個 Shell 數組變量的宣告方式,使用大括號 {}

大括號宣告陣列(array)還可以指定「間距」,格式為 {first..last..step},其中 step 就代表了間距;範例如下


for VAL in {0..10..2}; do; echo "$VAL"; done

image

默認 Shell 變量

● 默認情況下,Bash shell 會用一些特定的變量來定義系統環境 (這些特定變量就是默認 Shell 變量);以下列出幾個常見變量(默認環境變量有很多,如有需要請查表)

這些默認變量並非一定有值,有可能為空

Shell 變量功能描述
HOME當前用戶目錄
IFS用來切割文本的字符串
PS1Shell 命令 主提示 符號
PS2Shell 命令 次提示 符號
PS3Select 命令 提示 符號
EUID當前用戶的有效用戶 ID
BASH當前 Shell 實例的全路徑名
BASH_ENVShell 腳本啟動前會嘗試運行該變量定義的 啟動文件
GROUPS當前用戶的群組

image

● 默認變量可以使用 set 指令列出

但同時也要注意,不是所有默認環境變量都會在運行 set 命令時列出

PATH 環境變量:暫時環境變量

● PATH 環境變量用於進行命令、程序查找的目錄;當使用外部命令時,就會去查找 PATH 變量中的路徑,並在這些路徑下找命令


echo $PATH

路徑之間使用 : 隔開

● 這裡來說明,如何暫定 PATH 變量

暫時環境變量的生命週期只會依存於 Shell 的命生週期,如果 Shell 結束,這個暫定的 PATH 變量也會結束;範例如下…


echo $PATH

## 新增路徑(使用 `:` 隔開)
PATH=$PATH:/home/alien/Desktop/test

## 再次查看 PATH 就會發現有新增
echo $PATH

● 如果希望 sub shell 也可以使用自己的 PATH,那在定義時就要加入 export 關鍵字


export PATH=$PATH:/home/alien/Desktop/test

Shell 的啟動 & 環境變量

如果要讓環境變量永久化,那就要將環境變量設定寫入 某些文件;所以這裡我們要先來 討論 Shell 的啟動 & 環境變量的關係

Shell 啟動一般來說有以下幾種方式

A. 登入式:登入帳號默認的登入 Shell;可以是透過 /bin/login, SSH ... 等等方式從中端登入,這些方式就稱為 登入式 Shell

每個帳號預設的 Shell

B. 交互式:非登入 Shell;如同登入帳戶後,在 Linux GUI 中開啟的 Shell

sub shell 的概念

C. 腳本式(又稱為非交互式):作為腳本運行的 Shell

腳本 Shell 其實就是文本,而文本在開頭就會指定 shell 類型;

e.g : #!/bin/bash

Shell 提示字元

● 我們可以透過設定 PS1 變量來改變當前 Shell 視窗的提示字元

● 替換字元不要使用 Shell 關鍵字 (不要用 <>&{}= ... 等等符號)

替換格式如下


PS1='<替換關鍵字>'

常用 替換關鍵字 如下

關鍵字功能
\u當前使用者名
\hHost 名稱
\!當前已輸入的指令號(History 統計數量)
\w當前完整目錄
\W當前目錄(只有目錄,沒有全路徑)
\$依照權限顯示,如果是使用者顯示 $ 符號,如果是管理者則顯示 ! 符號

# User 名稱
# Host 名稱
# 指令號
# 當前目錄
# 權限顯示
PS1='\u@\h(\!)\W\$'

登入 Shell:加載順序

● 登入的 Shell 會從「5 個」不同的文件中讀取命令,而這些文件的讀取也是有順序的,順序功能如下

A. /etc/profile 內會呼叫並讀取 /etc/profile.d 目錄,該目錄會保存多個 sh 腳本

文件功能補充
/etc/profile主啟動文件每個用戶登入時都會加載該文件

## 查找關鍵字
cat /etc/profile | gerp -A 10 profile

ls -laF /etc/profile.d/

可以發現腳本內會讀取 /etc/profile.d 目錄中所有 .sh 結尾的文件

B. 接下來就是登入用戶後(針對用戶,可根據需求訂製),會加載的啟動文件(每個發行版使用的文件不同)

加載順序文件補充
1$HOME/.bash_profile-
2$HOME/.bash_login-
3$HOME/.profile-
-$HOME/.bashrc該文件是透過其他文件被加載

並非每個文件都存在,大部分 Linux 發行版都只會使用這些文件的 1 ~ 2 個


## Ubuntu 22 中是透過 `$HOME/.profile` 文件加載 `$HOME/.bashrc`

cat ./.profile | grep bashrc

交互式 Shell

● 交互式 Shell 也就是 sub shell,它在是在登入後才啟動的 Shell,所以自然不會讀取 /etc/profile 文件

交互式 Shell 會讀取 $HOME/.bashrc 文件


cat .bashrc | grep -v -E "^#"

.bashrc 有幾個作用

A. 加載查看 /etc/bash.bashrc

B. 載入用戶設定的別名 (alias

非交互式 Shell

● 非交互式 Shell (Shell 腳本) 啟動時會先檢查 BASH_ENV 全局變量,並去讀取它的值去加載啟動文件


printenv BASH_ENV

BASH_ENV 不一定會有設定值

一般來說 sub shell 會導出父 shell 的變量來使用 (記得用 export)

永久化環境變數

● 一般來說可以寫在以下兩個文件中

寫入文件注意事項
/etc/profile更新系統版本後就會被刷掉,最好是改成在 /etc/profile.d 目錄下創建一個 .sh 文件
$HOME/.bashrc可適用於大多數 Shell,如果是非交互式 Shell 則要注意 BASH_ENV 設定

更多 Linux Shell 知識

探索 Debian 系統上的套件管理系統(PKMS 概念)| 套件倉庫

Linux 環境、服務:用戶、權限管理

Linux 環境、服務、管理

在這個主題中,你可以探索各種 Linux 環境、服務和管理相關的重要概念…

了解 Linux 組成:內核責任、GNU 工具、桌面環境 | 基礎版到發行版

在這裡,你將了解 Linux 系統的基本組成部分,包括內核、GNU 工具和桌面環境,並了解不同 Linux 發行版之間的差異

探索 X Window 服務與桌面環境:Linux 視窗 XWindow 服務、D-Bus 機制

這裡探討了 Linux 系統中 X Window 服務和 D-Bus 機制的工作原理,讓你深入了解桌面環境的背後運作方式

Linux 系統管理入門:安全性、用戶管理與權限設定指南

在這個指南中,你將學習如何管理 Linux 系統,包括安全性、用戶管理和權限設定,以確保系統的安全和有效運作

認識身份驗證與權限管理:UNIX UID、使用者驗證與 PAM | PAM 設定

這裡介紹了 UNIX 系統中的身份驗證機制和權限管理方式,包括 UNIX UID、使用者驗證和 PAM(Pluggable Authentication Modules)設定

Linux Shell 相關知識

Linux Shell 相關知識

這個主題涵蓋了各種與 Linux Shell 相關的知識,從基礎到進階都有…

Shell 基礎知識:包括 Shell 的基礎使用以及差異,還有變數以及計算

認識命令行:Shell 類型、命令差異 | Sub Shell 關係 | Builtin 命令

這裡介紹了命令行中不同類型的 Shell,以及各種 Shell 命令之間的區別和內建命令的用法

Shell 全局及區域變數、特殊變數及環境變數 | Shell 啟動順序 | Array 變數

學習如何在 Shell 中使用全局變數、特殊變數和環境變數,以及如何設置和管理這些變數

探索腳本與命令:Shell 腳本的必備相關知識 | 腳本的數學運算

這裡介紹了 Shell 腳本的基本知識和常用技巧,包括如何執行腳本和進行數學運算

Shell 結構化

Shell 腳本程式中的條件語句和高級特性 | Shell 結構化 & 判斷

該篇文章是介紹結構化腳本的基礎以及特殊的高級技巧,轉著於腳本中的邏輯判斷

掌握 Bash 腳本中的迴圈與循環控制技巧 | Shell 結構化 & 循環

接著是腳本中的循環與控制的技巧,它可以讓我們在 Shell 腳本中有更多的判斷與技巧

探索 Shell 函數與腳本庫 | Shell 結構化 & 函數

這裡你將會學在到結構化腳本的重要技巧「函數」,它可以讓你建立可重複利用的腳本,以節省我們之後開發的時間

Shell 掌握參數處理與用戶輸入技巧 | Shell 結構化 &輸入互動

不可互動的腳本有時候相對無趣,這篇文章有分享如何讓腳本與使用者產生交互互動,並依照使用者輸入的參數做讀取

Shell script 進階

Shell 命令輸入與輸出指南:掌握標準文件描述符與重定向 | 臨時文件

掌控 Shell、指令的輸出輸入是成為進階使用 CLI 之人必經的一課,透過這篇文章可以了解到輸出輸入、臨時文件等等資訊

探索 Linux 訊號與後台進程管理:安排定期啟動腳本 | 運行時啟動腳本

信號可以算是 Shell 腳本與 Linux 系統之間的通訊方式,透過這個文章,我們可以了解到信號與 Shell 腳本的安排時間

Linux 硬體規劃:檔案系統、分區

Linux 硬體規劃:檔案系統、分區

最後,這個主題涵蓋了 Linux 系統中硬體結構相關的重要概念…

Shell 文件和目錄操作的常用命令和技巧 | 尋找檔案

這裡介紹了 Shell 中常用的文件和目錄操作命令,包括如何尋找文件和管理文件系統

深入探索 Linux 文件系統與硬體管理 | 分區、檔案系統、邏輯卷 | inode

學習如何管理 Linux 文件系統,包括分區、文件系統和邏輯卷等相關概念,以及文件系統中的 inode 機制

理解 Unix/Linux 設備偵測、建立 | 認識與使用 udevd | SCSI 與 Linux 核心

這裡介紹了 Unix/Linux 系統中的設備偵測和管理,包括如何使用 udevd 和 SCSI 相關的 Linux 核心知識


Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響