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

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

Overview of Content

本文將介紹 Shell 腳本中在讀取選項(Option)跟變數時常用的指令

無論是從基礎的 basenameshift 指令開始,還是深入探討處理 options 的不同方法,包括手動處理、使用 getoptgetopts 命令,都將一覽無遺。此外,您還將了解如何使用 read 命令從鍵盤或 Pipe 中讀取輸入

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

個人程式分享時比較注重「縮排」,所以可能不適合手機的排版閱讀,建議切換至「電腦版」、「平板版」視窗看


處理 Shell 參數

以下介紹如何透過特定的命來來取得輸入給 shell 的參數;參數輸入給 Shell script 的方式如下所示


# 腳本名為「hello_script.sh」 
# 參數為 「10 Apple 20 Banana」

./hello_script.sh 10 Apple 20 Banana

而接下來我們就要去獲取輸入給 hello_script.sh 腳本的參數

basename 命令:取得腳本名、基礎名

● 我們知道腳本名參數是 $0,可以透過它取得腳本名稱;當使用者傳入完整路徑時,我們 可以用 basename 命令取出 不包含路徑的腳本名

也就是 basename 命令可以單取出腳本、參數的基礎名稱


#!/bin/bash

# 包含相對路徑來取得腳本名
echo "The script name: $0"

# 不包含相對路徑,只輸出腳本名稱
echo "The script name, and use 'basename': $( basename $0 )"

# 為輸入 $1 參數
name=$( basename $1 )

# 使用 basename 取出參數的腳本名
echo "The total path($1), and use 'basename': $name"

以下範例腳本名為 ./read_cmd_2.sh,參數為 $PWD/read_cmd_2.sh

basename 也可以 用來消除副檔名,使用方式如下

以下範例直接使用 basename 命令,並對其輸入兩個參數 hello.sh.sh


basename hello.sh .sh

更近一步使用的範例:使用它來轉換所有 gif 成為 png


#!/bin/sh

for file in *.gif; do

    if [ ! -f $file]; then
        echo "The ($file) is not a file."
    fi

    fileName=$(basename $file .git)

    echo "Converting $fileName.git to $fileName.png"

    giftopnm $fileName.git | pnmtopng > $fileName.png

done

shift 命令:移動輸入變量

shift 命令的功能

將除了腳本名 ($0) 之外的參數全部向左移動一個位置;這個命令最常用在 當我們不知道使用者對腳本輸入多少參數時,我們可以一個參數一個參數處理、判斷

shift 預設的偏移量為 1,但它也可以一次性偏移多個位置


shift [偏移量=1]

shift 移除的注意事項

被移除的參數就不存在,無法再次取得,如果有顧慮的話可以先用副本參數本存(可以存為另一個參數)

shift 命令的使用範例

以下範例,一次移動輸入參數的兩個偏移量


#!/bin/bash

varCount=0

while [ -n "$1" ] ; do 
    (( varCount++ ))
    echo "Parameter #$varCount = $1"
    # 移動兩個參數
    shift 2
done

從下圖結果中,我們也可以看到,對腳本輸入的參數為 1 2 3 a b c,如果一次移動兩個偏移量時,就會輸出為 1 3 b


處理 Shell options

在使用其他命令時常常會有 options 功能(可選功能),腳本接到 options 功能後會依照設定去處理

e.g: ls -l

其中 ls 是命令,而 -l 就是 options

標準 Options

● Linux 每個命令的指令 options 的意義都不同,但它有一些大部分 options 代表的意義(不代表全部),我們可以透過這些標準 options 來猜測這個命令的使用

options說明
-a顯示所有對象
-c生成一個計數
-d指定一個目錄
-e擴展一個對象
-f指定讀入數據的文件
-h顯示命令的幫助信息
-i忽略文本大小寫
-l產生輸出的長格式版本
-n使用非交互模式(批處理)
-o將所有輸出重定向到的指定的輸出文件
-q以安靜模式運行
-r遞歸地處理目錄和文件
-x排除某個對象
-y對所有問題回答 yes

命令完整的 options 建議使用 man or --help 查詢

手動處理 options:使用 while、case、shift

● 使用 1. while 語句、2. case-in 語句、3. shift 命令,組成不斷接收判斷 options 的腳本

以下範例會取出所有對腳本的輸入,並且 case 先指定處理 -a-b 這兩個 options,其他的 options 則輸出 illegal option


#!/bin/bash

# 參數不為空的狀況,就持續執行 while
# `-n` 是 字串長度非 0
while [ -n "$1" ] ; do

    case "$1" in
        -a) echo "Get option of -a" ;;
        -b) echo "Get option of -b" ;;
         *) echo "$1 is not illegal option" ;; 
    esac
    # 位移輸入參數
    shift

done

從下圖中,我們可以看到輸入的參數為 -a -b -c -d 這些 options,而處理的則只有 -a -b 兩個 options

雙破折號:分離 options & 參數

● Linux Shell 中的雙破折號(--)功能:

用來表示 options 列表已經結束,之後的皆是參數,概念程式如下所示…


# 命令 `add`
# option `-a`
# params `1`、`2`

add -a -- 1 2

● 添加了判斷雙破折(--)符號,並在判斷是雙破折號後就 break 迴圈,代表已經處理完全的 options,接下來要處理的就是參數;範例如下…


#!/bin/bash

# 處理 Options
while [ -n "$1" ] ; do

    case "$1" in
        -a) echo "Get option of -a" ;;
        -b) echo "Get option of -b" ;;
        # 處理完 Options
        --) shift
            break;;
         *) echo "$1 is not illegal option" ;; 
    esac
    # 位移,移除處理完畢的 option
    shift

done


# 開始處理參數,透過 $@ 取得剩餘的所有參數
for param in $@ ; do
    echo "param: $param"
done

以下範例會分為「Options」、「參數」兩個分開管理

下圖中,我們對腳本 handle_options_2.sh 輸入… Options -a -b -c、參數 11 22 33,並且中間使用 -- 號隔開

處理 options 指定的參數

● 當 option 之後有指定的參數,那在讀取完 options、參數之後,記得使用 shift 命令位移

以下範例,我們會對 handle_options_3.sh 腳本輸入幾個 Option,而我們就是要取得指定 Option 之後的參數,指定 Option 如下

Option -a 之後 1 個參數

Option -b 之後 2 個參數為


#!/bin/bash
# `handle_options_3.sh` 腳本

while [ -n "$1" ] ; do

    case "$1" in
        -a) echo "Get option of -a, param=($2)"
            # 取得 -a 之後的參數後,使用 shift 移除參數
            shift ;;
        -b) echo "Get option of -b, param=($2, $3)"
            # 取得 -b 之後的兩個參數後,使用 shift 移除兩個參數
            shift 2 ;;
        --) shift
            break ;;
         *) echo "$1 is not illegal option" ;; 
    esac
    # 移除處理完畢的 option
    shift

done


for param in $@ ; do
    echo "param: $param"
done

下圖中,我們對handle_options_3.sh 腳本輸入,Option -a 之後的參數為 27、Option -b 之後的參數為 34 "Hello"

getopt 命令:格式化輸入

getopt 命令是一種 格式化字串的命令(將字串依照命令格式進行格式化)getopt 命令格式如下

opstring 在可以指定需要的 options 格式


getopt opstring parameters

接下來解釋如何使用

A. getoptopstring 用來格式化輸入,也就是指定如處理輸入


# 規定 options 有:`a`、`b`、`c`、`d` 四個
# 
# `b:` 代表 b 後面有一個參數,而 `a`、`c`、`d` 選項後沒有參數

getopt ab:cd -a -b test1 -cde test2 test3

B. getopt 添加 -q:可以省略錯誤資訊


getopt ab:cd -a -b test1 -cde test2 test3

● 如果要使用長指令 getopt 命令要修改為 getopt -q -a --long-option-name

set 替換輸入命令

set 命令中有一個選項(options)就是是雙破折號(--),它會將命令行參數替換成 set 命令的命令行值


#!/bin/bash

echo "param: $1"

set -- "Hello world"

echo "after set, param: $1"

也就是說使用 set -- <替換數值>,這個「替換數值」就會替代原來的輸入數據;使用 set -- 的範例如下

下圖中,我們對 set_cmd.sh 腳本輸入 123 參數,在經過命令替換後,我們可以看到輸入命令被改為 "Hello world"

getopt 命令的使用範例

在這個腳本中,我們先使用 set -- 命令替換,替換使用者的輸入為 getopt -q a:b: "$@"這個動作就是在做輸入參數格式化

格式化輸入後就可以用來上面的 while、case、shift 命令來處理輸入


#!/bin/bash

# 格式化輸入
set -- $( getopt -q a:b: "$@" )

echo "After getopt: $*"

# 處理 Options
while [ -n "$1" ] ; do

    case "$1" in
        -a) echo "Get option of -a, param=($2)"
            shift ;;
        -b) echo "Get option of -b, param=($2)"
            shift ;;
        --) shift
            break ;;
         *) echo "$1 is not illegal option" ;; 
    esac
    shift

done

# 處理剩餘的參數
for param in $@ ; do
    echo "param: $param"
done

getopt 無法讀取超過一個參數,讀取超過一個參數會出錯!

getopts 命令:加強 getopt 命令、自動處理輸入

請注意這個命令與 getopt 命令不同,它多了 s

getopts 命令,它有幾個特點… 首先先來看看 getopts 命令的格式


getopt opstring parameters

A. 變數的處理 不再透過 set -- 處理getopts 命令會自動將參數一個一個處理,參數處理結果成功會產生一個退出碼 0

getopts 會使用「opt 關鍵字」來處理使用者輸入的字串

使用 getopts 命令後不需要使用 shift 移動

B. 使用環境變量 $OPTIND$OPTIND 表示當前處理到哪個變數的 index

$OPTIND 從腳本名 ($0) 從 1 開始算;算到 參數結束

C. 使用環境變量 $OPTARG$OPTARG 表示當前正在處理的變數

只有有 option 時 $OPTARG 才會有值

D. 如果要省略錯誤只需要在 opstring 前,添加 : 符號

getopts 命令的使用範例


#!/bin/bash

while getopts :ab: opt ; do 
    echo "Current index: $OPTIND, arg: $OPTARG"

    case "$opt" in
        a) echo "Get the -a options" ;;
        b) echo "Get the -b options, param: $OPTARG";;
        *) echo "Other options: $OPTARG" ;;
    esac

    echo
done

getopts 仍無法取得一個 options 多個參數的狀況

getopts 自動分離 options 與 參數

getopts 在分析 options、參數的時候,options 與 參數 不需要空格

e.g: 我可以下命令 ./getopts.sh -a -b2,同樣可以分析出 options -b 之後接的參數是 2


取得用戶輸入

在交互式版本的腳本中常常需要使用者輸入資料,這時我們就需要取得用戶輸入,再分析使用者輸入的數據

read 命令:取得鍵盤輸入

read 命令

該命令可以主動反應到 Shell 要求使用者輸入資料,並讀取到腳本中,其格式如下


read options local_params

常用 Options 如下表

Options說明
-p添加輸入提示
-t以秒為單位,計時限制使用者的輸入時間;超時後返回一個非零狀退出態碼
-n<字符數量>讀取指定的字符數量
-s安靜模式輸入(不會顯示使用者輸入的資訊),同常使用在輸入密碼

read 命令使用範例

A. read 基礎使用


#!/bin/bash

echo -n "Enter target file: "

read file_name

echo "Your target file is $file_name"

B. 使用輸入提示 (-p)、限制輸入時間 (-t)


#!/bin/bash

echo -n "Enter target file: "

read -t 5 -p "(In 5s): " file_name

if [ -z $file_name ] ; then
    echo
    echo "Input data too slow."	
else 
    echo "Your target file is $file_name"
fi

C. 讀取指定字符數量 (-n)


#!/bin/bash

read -n1 -p "Accept(Y/N): " res

echo 

case $res in
    Y|y)
        echo "Ok, start doing." ;;
    N|n)	
        echo "Quit!"
        exit ;;
    *)
        echo "Unknow options($res)" ;; 
esac

echo "Finish script."

D. 安靜模式輸入 (-s)


#!/bin/bash

read -s -p "Input passwd: " pw

echo
echo "Input passwd: $pw"

read 命令:從 Pipe 讀取輸入

● Read 可以讀取 Pipe 文本直到換行(以換行做為分隔),透過這個特性就可以逐行讀取文件文本,範例如下:

以下範例透過 cat 來讀取文件,並輸入給 read 命令


#!/bin/bash

count=1

cat testFile | while read lineTxt ; do 
    echo "Line $count: $lineTxt"
    (( count++ ))
done


更多 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?

發表迴響