shell腳本編程筆記(五)—— 輸入處理

一、 命令行參數

特殊環境變量 含義
$1 $2..$9,${10}... 表示腳本的第n個參數

$0

basename $0

腳本名(含路徑)

腳本名(不含路徑)

$#

${!#}

參數總數

最後一個參數值

$*

將所有參數作爲一個字符串保存

$@ 將n個參數作爲n個字符串保存

向shell腳本傳遞數據最基本的方法是使用命令行參數,在腳本運行時指定參數,例如:

./add.sh 10 30
./output.sh 'Hello World' #帶空格的參數需加引號,單雙引號均可

shell提供位置參數(一組特殊環境變量集合),分別用$1 $2..$9表示第一至第九個參數,10之後寫法爲${10}。

有兩個比較特殊的參數$0和$#,$0表示執行的腳本名(含路徑),$#表示參數的個數

如果只想獲取腳本名不需要路徑,可以使用basename命令

當腳本需要輸入參數才能正常工作時,應該使用 if [ -n $1] 或者 if [ $# = 2] 判斷參數數量是否正確。

你可能會想,既然$#表示參數的個數,${$#}是不是就代表最後一個參數的值?實際上不是,shell不允許在{}中使用$符。正確的寫法應該是${!#}。

想要獲取所有命令行參數,可以使用$*或$@,$*將所有參數視爲一個字符串,$@則將每個參數視爲一個字符串。

#!/bin/bash
echo "print each param from \"\$*\""
for var in "$*"
do
    echo "$var"
done
echo "print each param from \"\$@\""
for var in "$@"
do
    echo "$var"
done

運行腳本

./test.sh a b c d
print each param from "$*"
a b c d
print each param from "$@"
a
b
c
d

二、 移動變量 shift

shift命令默認會將每個變量向左移一位(相當於shift 1),$3->$2,$2->$1,並刪除原$1的值。當你不確定到底會有幾個參數時,這是個好辦法,你可以只操作第一個變量,移動參數,然後繼續操作新的第一個變量。

#!/bin/bash

# 找出文件(可指定多個文件名)中長度最長的單詞,$1爲文件名
while [[ -n $1 ]]  # 參數不爲空,即還有待查找文件
do
    if [[ -r $1 ]];then # 文件存在且有讀權限
        max_word=
        max_len=0
        for i in $(strings $1) # strings程序(包含在binutils包中)爲每一個文件產生一個可讀的文本格式的words列表
        do
            len=$(echo $i | wc -c) # wc -c統計字符數,即計算每個單詞長度
            if ((len > max_len));then
                max_len=$len
                max_word=$i
            fi
        done
        echo "$1:'$max_word' ($max_len characters)"
    fi
    shift # 參數向左偏移,即開始查找下一個文件
done

也可以利用 shift n 指定每個變量向左移幾位

#! /bin/bash

while [ -n "$1" ] # 加雙引號表示強制變量爲字符串格式,對於字符串的比較,變量取值一定要加雙引號
do
echo $1
shift 2
done

 

三、 選項處理

1. 單獨選項

有時腳本後不僅有參數,還有選項,例如

./mypara.sh -a -b param1 -d

最簡單的可以使用case處理選項,確定哪些選項後可能有參數,會有幾個參數,在對應case的中處理參數。

#! /bin/bash

# 假設選項有-a -b -c,僅-b後會有1個參數
while [ -n "$1" ]
do
  case "$1" in
     -a) echo "Found the -a option";;
     -b) param="$2"
         echo "Found the -b option,with parameter $param"
         shift ;; #參數多佔一位,需要挪走
     -c) echo "Found the -c option";;
     --) shift
      break ;;
      *) echo "$1 is not the an option";;
  esac
  shift
done

 

2. 合併選項與getopt命令

合併選項例如 ll -rth,這時前面的方法就沒法解決問題,需要使用到 getopt 命令。

getopt 能夠識別一系列任意形式的選項和參數,並自動將它們轉爲適當格式。

getopt optstring parameters

optstring 是其中的關鍵,它定義了命令行有效的選項字母,以及哪些選項需要參數(在字母后加:),例如:

getopt ab:cd -a -b test1 -cd test2 test3
#選項有-a -b -c -d,-b後有:說明-b後面會有參數

輸出會是轉換後的格式,其中test2 test3被識別爲額外參數,用--分隔開

如何在腳本中使用getopt 命令?可以將getops命令輸出(格式化後的參數)傳給set,set -- 命令會將命令行參數替換成set命令的命令行值。

#/bin/bash
###################################
# Extract command line options & values with getopt

set -- $(getopt -q ab:cd "$@")
 
while [ -n "$1" ]
do
  case "$1" in
  -a) echo "Found the -a option" ;;
  -b) param="$2"
      echo "Found the -b option, with parameter value $param"
      shift ;;
  -c) echo "Found the -c option" ;;
  --) shift
      break ;;
   *) echo "$1 is not option";;
esac

  shift
done

#輸出額外參數
count=1
for param in "$@"
do 
  echo "Parameter #$count: $param"
  count=$[ $count + 1 ]
done

你會發現這個腳本跟前面整體差別不大,但它提供了合併選項的處理。

但是,getopt 命令並不擅長處理帶空格和引號的參數值,例如

./mypara.sh -a -b param1 -c "test1 test2" test3

可以看到它並沒有將"test1 test2"當作整體處理,只是用空格作分隔符。

 

3. 更強大的getopts命令

getopts基本上是一個增強版:

  • getopt 命令處理選項和命令行後只生成一個格式化的輸出,需要用set轉換,而getopts不再需要。
  • getopt 命令不擅長處理帶空格和引號的參數值,而getopts可以
# 用法與getopt基本相同
getopts optstring variables
# 要忽略錯誤消息,需在optstring前加冒號:,即
getopts :optstring variables
# variables中會保存當前參數

注意getopts解析後的命令行選項不帶-,使用case匹配時也不需加,$OPTARG中存儲選項後參數

#!/bin/bash
###################################
# simple demonstration of the getopts command
#
echo 
while getopts :ab:c opt
do
  case "$opt" in
  a) echo "Found the -a option" ;;
  b) echo "Found the -b option, with parameter value $OPTARG" ;; # $OPTARG中存儲選項後參數
  c) echo "Found the -c option" ;;
  *) echo "Unknown option: $opt" ;;
  esac
done

可以將選項和參數合在一起,中間不加空格;還可以將所有未定義參數統一輸出成問號?

 

四、 獲得用戶輸入

read命令可從標準輸入(鍵盤)或另一個文件中接收輸入,並保存到一個變量,下面來看其常用用法。

1. 基本讀取

最簡單的用法,-p會顯示指定的輸入提示符

#! /bin/bash

read -p "Input your name: " name
echo "Hello $name"

可以輸入多個參數

#! /bin/bash

read -p "Input your name and age: " name age
echo "Hello $name,age $age"

如果變量數>輸入參數,會從前往後分配,後面的變量爲空;如果變量數<輸入參數,多餘的參數會全存在最後一個變量

若不指定參數,read會將接收到的數據存入特殊變量REPLY中

#! /bin/bash

read -p "Input your name: "
echo "Hello $REPLY"

 

2. 超時設置 -t

如果用戶一直不輸入,read默認會一直等,-t選項可以設置定時器指定等待秒數,超時後read命令會返回非0退出狀態碼。

#! /bin/bash

if read -t 5 -p "Input your name: "
then
   echo "Hello $REPLY"
else
   echo #避免Timeout直接輸出在提示語句後面
   echo "Timeout"
fi

 

3. 輸入指定字符數後自動退出 -n

read命令的 “-n數字” 選項可指定在用戶輸入指定字符數後自動退出,無需按回車。

#! /bin/bash

read -n1 -p "Please input a character: "
echo
echo "Your input is $REPLY"

 

4. 隱藏輸入 -s

有時用戶希望將輸入傳入腳本但又不在屏幕上顯示,典型情況就是輸密碼。read命令的-s選項就可以做到(實際也會顯示,只是read命令將其改爲了與屏幕底色相同)。

#! /bin/bash

read -s -p "Please input your password: "
echo
echo "Is your password really $REPLY"

 

5. 從文件中讀取

每次調用read命令會從文件中讀取一行文本,當文件中再沒有內容時,read會以非0狀態碼退出。最常見的方法是cat文件,將結果通過管道傳給通過while命令的read命令。

#! /bin/bash
# reading data from a file

count=1 
cat breaktest.sh | while read line
do
  echo "Line $count: $line"
  count=$[ $count + 1 ]
done
echo "--- end of file ---"

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章