美思 [Shell Scripting] 教學:處理命令列參數

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

命令列參數的目的在改變命令列工具的行為,以符合當下的需求。本文說明如何在 shell script 中處理命令列參數。

命令列參數的資料結構

命令列參數是以空白隔開的串列。在 shell script 中以特殊變數 $@ 來代表。

這裡使用 for 迴圈逐一走訪命令列參數:

#!/bin/sh

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

exit 0;

輸入五個參數時,正確地將參數逐一印出:

$ ./params a b c d e
a
b
c
d
e

但參數中有空白符時,會錯誤地將該參數拆開:

$ ./params "a and b" c d e
a
and
b
c
d
e

這不是預期中的結果,"a and b" 錯誤地拆成三個參數。

應對的方式是用一對雙引號 " 把特殊變數 $@ 包起來:

#!/bin/sh

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

exit 0;

這時候命令列參數會正確地拆開:

$ ./params "a and b" c d e
a and b
c
d
e

另外一種方式是改用 while 迴圈來走訪參數。根據參數數量 $# 來決定迴圈的終止條件,每次迭代用 shift 取出一個參數:

#!/bin/sh

while [ $# -gt 0 ];
do
    echo $1;
    shift;
done

exit 0;

這時也可正確地拆解參數:

$ ./params "a and b" c d e
a and b
c
d
e

自行解析命令列參數

先前的例子僅是展示參數的資料結構,但未處理參數。本節展示一個處理參數的虛擬程式:

#!/bin/sh

program=$0;
version="0.1.0";

while [ $# -gt 0 ];
do
    _param=$1;

    case $_param in
        "-v"|"--version")
            echo $version;
            exit 0;
            ;;
        "-h"|"--help")
            echo "Usage: $program path/to/file ...";
            exit 0;
            ;;
        -*)
            echo "Unknown parameter: $_param" >&2;
            exit 1;
            ;;
        *)
            break;
            ;;
    esac
done

while [ $# -gt 0 ];
do
    echo $1;
    shift;
done

exit 0;

這個程式使用兩個 while 迴圈。第一個 while 迴圈處理選擇性參數,並對各個參數執行相對應的動作。第二個 while 迴圈則逐一走訪完剩下的參數。

本範例程式未使用其他工具,以純手動的方式解柝命令列參數。

傳遞命令列參數給另一個程式

有時候,命令列工具會呼叫另一個命令列工具。這時要如何區分參數是傳給那個命令列程式呢?約定俗成的方式是以 -- 做為命令列參數的分隔符。在 -- 前的參數會傳給原程式,在其後的參數則會傳給目標程式。

ccrun 是 shell 寫成的小工具,其功能為快速地編譯和執行 C 和 C++ 程式碼,不需要寫 Makefile。對於小型的練習程式碼來說相當方便。

執行 ccrun 的虛擬指令如下:

$ ccrun path/to/*.c -- --version

-- 後的參數 --version 不是傳給 ccrun 本身,而是傳給自動編譯出來的命令列程式。

這裡節錄出 ccrun 一部分的程式碼:

# Excerpt.

args="";
is_arg=0;
for arg in $@;
do
    if [ "$arg" = "--" ];
    then
        is_arg=1;
        continue;
    fi

    if [ $is_arg -eq 1 ];
    then
       args="$args $arg";
    fi
done

if ! LD_LIBRARY_PATH=$LD_LIBRARY_PATH ./$dest $args;
then
    clean;
    exit 1;
fi

由此可看出,在 ccrun 偵測到 -- 時,開啟旗標 is_arg,並收集後續的參數。然後將收集到的參數丟給目標程式來執行。

關於作者

身為資訊領域碩士,美思認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

美思喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,美思將所學寫成文章,放在這個網站上和大家分享。