리눅스 명령행 인자: 명령어 옵션과 따옴표, 옵션 끝 표시, $1, $# 받기
Linux 命令列引數(command-line argument)是寫在指令名稱後面、以空白隔開並傳給程式的值。在 AlmaLinux 9 伺服器上,把選項、引號、萬用字元和 -- 傳給一個會原樣印出所收到引數的測試指令碼後發現,切分引數的是 shell,讀取選項的則是各個指令自己。
下方輸出是在 AlmaLinux 9.8 伺服器的 root 帳號以 bash 5.1 執行的原始結果。測試指令碼與範例檔案另外建立在工作資料夾中,路徑 /home/user/www 為範例值。
1️⃣ 什麼是 Linux 命令列引數?
在命令列寫下像 [指令 引數1 引數2] 這樣的內容時,shell 會以空白為界切分單字。第一個單字成為要執行的程式,其餘單字依序作為引數傳給該程式。
為了親眼看到傳入的引數,我們在工作資料夾建立了 args.sh。$0 存放執行時用的名稱,$# 存放引數個數,從 $1 起依序存放各個引數。方括號是為了看清每個引數的開頭與結尾而加上的。
#!/bin/bash
echo "\$0 = $0"
echo "\$# = $#"
i=1
for a in "$@"; do
echo "\$$i = [$a]"
i=$((i+1))
done替指令碼加上執行權限後,傳入 a、b、c 三個單字執行。
./args.sh a b c$0 = ./args.sh
$# = 3
$1 = [a]
$2 = [b]
$3 = [c]三個單字分別進入 $1、$2、$3,$# 為 3。若在 bash 後面寫上指令碼名稱來執行,$0 會照寫下的名稱放入 args.sh。
bash args.sh a b c$0 = args.sh
$# = 3
$1 = [a]
$2 = [b]
$3 = [c]只有 $0 變成 args.sh,三個引數與個數都和前面的結果相同。
2️⃣ 命令列引數與指令選項
Linux 指令通常依 [指令 -選項 引數] 的順序書寫。選項改變指令的運作方式,引數則指定指令要處理的檔案、路徑或值。
ls -l /home/user/www對 shell 而言,-l 也只是一個引數。把相同的單字傳給 args.sh 後,-l 進入 $1,路徑進入 $2。
./args.sh -l /home/user/www$0 = ./args.sh
$# = 2
$1 = [-l]
$2 = [/home/user/www]把 -l 當成選項來讀的是 ls。所以同一個字母在不同指令中意思不同:head 的 -n 指定要輸出的行數,ls 的 -n 則以編號而非名稱顯示擁有者。
有些選項會把緊接在後的單字當成自己的值。在 find 指令中,-type 以 f、-name 以 "*.txt" 作為自己的值,像這樣附屬於選項的值稱為選項引數。
find . -type f -name "*.txt"這個指令最前面的 . 是不附屬於任何選項的引數,用來指定開始搜尋的資料夾。
3️⃣ Linux 指令選項的寫法
以一個連字號開頭的短選項只有一個字母,而且可以多個連寫。首先用 [ls -l -a] 列出工作資料夾中的 site 資料夾。
ls -l -a sitetotal 8
drwxr-xr-x 3 root root 52 Oct 28 16:00 .
drwxr-xr-x 3 root root 137 Oct 29 01:20 ..
-rw-r--r-- 1 root root 17 Oct 28 16:00 .htaccess
drwxr-xr-x 2 root root 23 Oct 28 16:00 css
-rw-r--r-- 1 root root 14 Oct 28 16:00 index.html接著用 diff 與兩個字母連寫的 [ls -la] 的輸出比較。
diff <(ls -l -a site) <(ls -la site) && echo samesame兩份輸出連一個字元都沒有差別。以兩個連字號開頭的長選項寫成單字,ls 的 --all 與 -a 是同一個選項,所以 [ls -l --all site] 也得到相同結果。
長選項只寫開頭部分,只要能確定是唯一一個選項就會被接受。--al 同時符合 --all 與 --almost-all 兩個選項,因此 ls 沒有輸出清單,並以結束代碼 2 結束。
ls --al sitels: option '--al' is ambiguous; possibilities: '--all' '--almost-all'
Try 'ls --help' for more information.需要值的選項有好幾種寫法。用 head 輸出 a.txt 的前兩行時,短選項可以把值直接接上,寫成 [-n2]。
head -n2 a.txtalpha
beta長選項用等號接上值,寫成 [--lines=2]。
head --lines=2 a.txtalpha
beta連同以空白隔開的 [-n 2] 與 [--lines 2],四種寫法都輸出了 alpha 和 beta 兩行。
ls、head、cat 都用 C 函式庫的 getopt_long 函式讀取選項。用 strings 查看這三個程式檔,每個都含有這個函式名稱。
要在 shell 中確認相同的規則,可使用 util-linux 的 getopt 指令。在 -o 後面寫短選項、在 -l 後面寫長選項,字母後面的冒號表示該選項需要值。
getopt -o lan: -l all,lines: -- -la -n 2 --all a.txt -l -a -n '2' --all -- 'a.txt'連寫的 -la 被拆成 -l 與 -a,-n 2 被合併為 -n 與值 2。不是選項的 a.txt 被移到最後的 -- 之後。
4️⃣ 用引號括住的命令列引數與萬用字元
含有空白的檔名必須用引號括住,才會作為一個引數傳入。
./args.sh "my file.txt" report.txt$0 = ./args.sh
$# = 2
$1 = [my file.txt]
$2 = [report.txt]不加引號時,my 和 file.txt 被分開,$# 變成 3。若這樣傳給 ls 或 cat,就會去找名為 my 和 file.txt 的兩個檔案。
./args.sh my file.txt report.txt$0 = ./args.sh
$# = 3
$1 = [my]
$2 = [file.txt]
$3 = [report.txt]萬用字元會在程式收到之前由 shell 換成檔名。在工作資料夾傳入 *.txt 後,以 .txt 結尾的四個檔案依名稱順序傳入。
./args.sh *.txt$0 = ./args.sh
$# = 4
$1 = [a.txt]
$2 = [b.txt]
$3 = [my file.txt]
$4 = [report.txt]用引號括住的 "*.txt" 不會被 shell 替換,原封不動成為一個引數。像 find 的 -name 這種由程式自己比對模式的選項,就要這樣用引號括住再傳入。
./args.sh "*.txt"$0 = ./args.sh
$# = 1
$1 = [*.txt]空的引號也會作為一個空引數傳入。執行 [./args.sh '' b] 後,$# 為 2,$1 為空。
5️⃣ 指令選項結束標記(--)
像名為 -n 的檔案這樣以連字號開頭的引數,會被指令當成選項讀取。在只放了這個檔案的 dash 資料夾執行 [ls -n] 時,ls 不把 -n 當檔名,而是當成編號輸出選項,並列出了資料夾內容。
ls -ntotal 4
-rw-r--r-- 1 0 0 10 Oct 28 16:00 -n寫在 -- 之後的引數即使以連字號開頭,也不會被當成選項。[ls -- -n] 只輸出了名為 -n 的檔名。
ls -- -n-n即使在同一個指令中,-- 前後相同的字元也會被讀成不同的東西。把 -n 傳給 cat 兩次後,前面的 -n 被讀成行號選項,-- 後面的 -n 被讀成檔名,輸出了加上行號的檔案內容。
cat -n -- -n 1 dash file也可以加上路徑寫成 ./-n,這時引數不以連字號開頭,會被讀成同一個 -n 檔案。
把 -- 當成選項結束來讀,也是指令這一側的規則。傳給不讀選項的 args.sh 時,-- 也原樣進入了 $1。
./args.sh -- -a$0 = ./args.sh
$# = 2
$1 = [--]
$2 = [-a]POSIX 公用程式慣例規定,不是選項引數的第一個 -- 要被當成選項的結束。因此在遵循這套慣例的指令中,以 - 開頭的檔名要寫在 -- 之後。
6️⃣ 在 shell 指令碼中接收命令列引數
shell 指令碼透過位置參數與特殊參數讀取收到的引數。C 程式的 main(int argc, char *argv[]) 也接收相同的引數,兩者並排如下。
| 參數 | 存放的值 | C 語言 main 函式 |
|---|---|---|
| $0 | 執行的指令碼名稱 | argv[0] |
| $1 ~ $9 | 第一到第九個引數 | argv[1] ~ argv[9] |
| ${10} | 第十個起加大括號 | argv[10] |
| $# | 引數個數、不計 $0 | argc - 1 |
| "$@" | 全部引數、逐一分開 | 從 argv[1] 到最後 |
| "$*" | 以空白串成一個字串 | — |
Python 程式會以 sys.argv 清單接收相同的引數。用 [python3 -c] 傳入三個引數後,用引號括住的 my file.txt 進入清單的同一格,第一格則是 -c。
python3 -c 'import sys; print(sys.argv)' a "my file.txt" -l['-c', 'a', 'my file.txt', '-l']"$@" 與 "$*" 用雙引號括住時結果不同。為了看出這個差別,我們建立了依序輸出這兩種寫法的 star.sh。
#!/bin/bash
echo 'with "$@"'
printf ' [%s]\n' "$@"
echo 'with "$*"'
printf ' [%s]\n' "$*"以 a、"b c"、d 三個引數執行 star.sh。
./star.sh a "b c" dwith "$@"
[a]
[b c]
[d]
with "$*"
[a b c d]"$@" 把三個引數分開傳遞,"$*" 則以空白串成一個傳遞。
因此,要把收到的引數原樣傳給其他指令時,使用 "$@"。
shift 會丟掉 $1,把其餘引數各往前移一格。用在先讀一個選項、再把其餘當成檔案清單處理的指令碼中。
我們建立了在 shift 前後輸出 $1 與 $# 的 shift.sh。
#!/bin/bash
echo "before shift: \$1 = $1, \$# = $#"
shift
echo "after shift: \$1 = $1, \$# = $#"以 -v、a.txt、b.txt 三個引數執行 shift.sh。
./shift.sh -v a.txt b.txtbefore shift: $1 = -v, $# = 3
after shift: $1 = a.txt, $# = 2第十個引數起要像 ${10} 這樣用大括號括住。我們建立了並排輸出 $10 與 ${10} 的 ten.sh。
#!/bin/bash
echo "\$10 = $10"
echo "\${10} = ${10}"把 a 到 k 共 11 個引數傳給 ten.sh。
./ten.sh a b c d e f g h i j k$10 = a0
${10} = j$10 輸出為 $1 後面接 0 的 a0,${10} 則輸出第十個引數 j。
🔲 命令列引數模擬器
在下方的模擬器中,可以把引號、萬用字元和 -- 傳給 args.sh,也能自己輸入指令,看看 ls 與 getopt 如何讀取選項,以及 star.sh 與 shift.sh 如何接收引數。
🔢 FAQ & 推薦內容
ℹ️ 聯盟行銷說明
本站內容包含聯盟行銷連結。訪客透過這些連結購買商品或服務時,本站會從賣家獲得佣金。在這個過程中,購買者支付的金額(遇到活動折扣時金額會降低↓)並不會因此提高。刊登的價格、折扣與庫存資訊以撰寫當時為準,可能與實際情況不同,購買前請向賣家做最後確認。商品的挑選與評價依本站自訂的標準撰寫,是否支付佣金不會影響介紹順序或評價內容。