編寫shell腳步--讀取鍵盤輸入

一、read–從標準輸入讀取輸入值

內嵌命令read的作用是讀取一行標準輸入。此命令可用於讀取鍵盤輸入值或應用重定向讀取文件中的一行。read命令的語法結構如下所示:

read [-options] [variable...]

語法中options爲下表中列出的一條或多條可用的選項,而variable則是一到多個用於存放輸入值的變量。若沒有提供任何此類變量,則由shell變量REPLY來存儲數據行。

基本上,read命令將標準輸入的字段值分別賦給指定的變量。若使用read命令改寫之前的整數驗證腳本,如下所示:

#!/bin/bash

#read-integer: evaluate the value of a integer:

echo -n "Please enter an integer ->"
read INT
if [[ "$INT" =~ ^-[0-9]+$ ]]; then
			if [ "$INT" -eq 0 ]; then
						echo "INT is zero."
			else
						if [ $INT -lt 0]; then
								echo "INT is negative."
						else
								echo "INT is positive."
						fi
						if [ $((INT % 2)) -eq 0]; then
								echo "INT is even."
						else
								echo "INT is odd."
						fi
			fi
else
			echo "INT is not an integer." >&2
			exit1
fi

我們使用帶有-n選項(時接下來的輸出在下一行顯示)的echo命令來輸出一條提示符,然後使用read命令給INT變量賦值。

在下述腳本中,read命令將輸入值賦給多個變量。

#!/bin/bash

# read-multiple: read multiple values from keyboard

echo -n "Enter one or more values > "
read var1 var2 var3 var4 var5

echo "var1 = '$var1' "
echo "var1 = '$var2' "
echo "var1 = '$var3' "
echo "var1 = '$var4' "
echo "var1 = '$var5' "

此腳本可以給5個變量賦值並輸入。需要注意當輸入少於或多於5個值的時候,read的運作方式如下:
在這裏插入圖片描述
read命令讀取的值少於預期的數目,則多餘的變量值爲空,而輸入值的數目超出預期的結果時,最後的變量包含了所有的多餘值。
如果read命令之後沒有變量,則會爲所有的輸入分配一個shell變量:REPLY。

#!/bin/bash

#read-signal: read multiple values into default variable

echo -n "Enter one or more values > "
read

echo "REPLY = '$REPLY' "

以上腳步的運行結果如下所示。

$ read-single
Enter one or more values > a b c d
REPLY = 'a b c d'

1.1、選項

read選項

選項 描述
-a array 將輸入值從索引爲0的位置開始賦給array
-d delimiter 用字符串delimiter的第一個字符標誌輸入的結束,而不是新的一行的開始
-e 使用Readline處理輸入。此命令使用戶能使用命令行模式的相同方式編輯輸入
-n num 從輸入中讀取num個字符,而不是一整行
-p prompt 使用prompt字符串提示用戶進行輸入
-r 原始模式,不能將後斜線字符翻譯爲轉義碼
-s 保密模式。不在屏幕顯示輸入的字符。此模式在輸入莫馬和其它機密信息時很有用處
-t seconds 超時。在seconds秒後結束輸入。若輸入超時,read命令返回一個非0的退出狀態
-u fd 從文件說明符fd讀取輸入,而不是從標準輸入中讀取

使用不同的選項,read命令可以達成不同的有趣的效果。例如,可使用-p選項來顯示提示符

#!/bin/bash

# read-sibgle: read multiple values into default varible

read -p "Enter one or more values > "

echo "REPLY = '$REPLY' "

使用-t與-s選項,可以寫出讀取“祕密”輸入的腳本,此腳本若一定時間內沒有完成輸入,會造成超時。

#!/bin/bash

# read-secret: int a secret passphrase

if read -t -sp "Enter secret passphrase > " secret_pass; then
	echo -e "\nSecret passphrase = '$secret_pass' "
else
	echo -e "\nInput timed out" >&2
	exit 1
fi

上訴腳本會提示用戶輸入祕密通行短語,系統的等待時間爲10廟。若10秒內用戶沒有完成輸入,腳本以出錯狀態結束運行。又因爲使用了-s選項,輸入的密碼並不會顯示到屏幕上。

1.2、使用IFS間隔輸入字段

通常shell會間隔提供給read命令的內容。這也就意味着,在輸入行,由一到多個空格將多個單詞分隔成爲分離的單項,再由read命令將這些單項賦值給不同的變量。此行爲是由shell變量IFS(Internal Field Separator)設定的。IFS的默認值包含了空格、製表符和換行符,每一種都可以將字符彼此分隔開。
我們可以通過改變IFS值來控制read命令輸入的間隔方式。例如,文件/etc/passwd的內容使用冒號作爲字段之間的間隔符。將IFS的值改爲單個冒號即可使用read命令讀取/etc/passwd文件的內容,併成功將各字段分隔爲不同的變量。下面的腳本便完成了此功能。

#!/bin/bash

# read-ifs: read fields from a file

FILE=/etc/passwd

read -p "Enter a username > " user_name

file_info=$(grep "^$user_name:" $FILE) 

if [ -n "$file_info" ]; then
			IFS=":" read user pw uid gid name home shell <<< "$file_info"
			echo "User =                         '$user' "
			echo "UID =                           '$uid' "
			echo "GID  =                           '$gid' "
			echo "Full Name =              '$name' "
			echo "Home Dir =               '$home' "
			echo "Shell =                        '$shell' "
else
			echo "No such user '$user_name' " >$2
			exit 1
fi

此腳本提示用戶輸入系統賬戶的用戶名,根據用戶名找到/etc/passwd文件中相應的用戶記錄,並輸出此記錄的各個字段。此腳本包含了兩行有趣的代碼。第一行是file_info=$(grep "^$user_name:" $FILE),他將grep命令的結果賦值給file_info變量。grep命令使用的正則表達式保證了用戶名只會與/etc/passwd文件中的一條記錄相匹配。

第二行是IFS=":" read user pw uid gid name home shell <<< "$file_info",由三部分構成,即一條變量賦值語句、一條帶有變量名作參數的read命令和一個陌生的重定向運算符。首先讓我們看一下其中的變量賦值語句。

shell允許在命令執行之前對一到多個變量進行賦值,這些賦值操作會改變接下來所執行命令的操作環境。但是賦值的效果是暫時性的,只有在命令執行週期內有效。本例中,IFS的值被修改爲一個冒號。我們也可以通過以下方式達到此效果。

OLD_IFS = "$IFS"
IFS=":"
read user pw uid gid name home shell <<< "$file_info"
IFS="$OLD_IFS"

首先我們儲存了IFS的舊值,並將新值賦給IFS,我們執行了read命令最後將IFS恢復原值。顯然,對於做相同的事情,將變量賦值語句置於執行命令前,是更爲簡潔的方法。

操作符“<<<”象徵一條嵌入字符串。嵌入字符串與嵌入文檔類似,只不過更爲簡短,它包含的是一條字符串。本例將/etc/passwd文件中讀取的數據輸送給read命令。

注意:read不可重定向。通常read命令會從標準輸入中獲取輸入,而不能採用這種方式:echo "foo" | read

二、驗證輸入

在程序具備了讀取鍵盤輸入功能的同時,也帶來了新的編程上的挑戰–驗證輸入。通常來說,程序寫得好與不好的差別僅僅在於程序是否能夠處理突發意外情況。而意外情況經常以錯誤輸入的形式出現。對所有程序接收的輸入執行此類驗證是防禦無效數據的重要方式,且對於多用戶共享的程序尤其重要。只有那些執行特殊任務且只會被作者使用一次的程序,處於經濟學原理可以省略這些安全措施。儘管如此,若程序執行的是像刪除文件這樣的危險任務,爲以防萬一海斯進行數據驗證會比較好。
以下是一個對不同類型輸入進行驗證的程序。

#!/bin/bash

# read-validate: validate input

invalid_input () {
		echo "Invalid input '$REPLY' " >&2
		exit 1
}

read -p "Enter a single item > "

#input is empty (invaild)
[[ -z $REPLY ]] && invalid_input

#input is multiple items (invalid)
(( $(echo $REPLY | wc -w) > 1 )) && invalid_input

#is input avalid filename?
if [[ $REPLY =~ ^[  -[:alnum:]\._ ]+$ ]]; then
			echo "'$REPLY' is valid filename. "
			if [[ -e $REPLY ]]; then
						echo "And file '$REPLY ' exists. "
			else
						echo "However, file '$REPLY' does not exist."
			fi
			#is input a floating point number?
			if [[ $REPLY =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then
						echo "'$REPLY' is a floating point number."
			else
						echo "'$REPLY' is not a floating point number."
			fi
			#is input an integer?
			if [[ $REPLY =~ ^-?[[:digit:]]+$ ]]; then
						echo "'$REPLY' is an integer."
			else
						echo "'$REPLY' is not an integer."
			fi
else
			echo "The string '$REPLY' is not a valid filename"
fi

此腳本首先提示用戶進行輸入,然後分析確定用戶的輸入。腳本使用了shell函數、[[]]、(())、控制操作符&&、if以及適量的正則表達式。

三、菜單

菜單驅動是一種常見的交互方式。在菜單驅動的程序會呈現給用戶一系列的選項,並請求用戶進行選擇。例如,可想象程序呈現的菜單如下所示。

Please Select:

1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
4. Quit

Enter selection [0-3] > 

我們可以構建菜單驅動的程序來執行上述菜單中各項任務。

#!/bin/bash

# read-menu: a menu driven system information program

clear
echo "
Please Select:

1. Display System Information
2. Display Disk Space
3. Display Home Space Utilization
4. Quit
"

read -p "Enter selection [0-3]  > "

if [[ $REPLY =~ ^[0-3]$ ]]; then 
			if [[$REPLY == 0 ]]; then
					echo "Program terminated."
					exit
			fi
			if [[ $REPLY ==1 ]]; then
					echo "Hostname: $HOSTNAME"
					exit
			fi
			if [[ $REPLY == 2 ]]; then
					df -h
					exit
			fi
			if [[ $REPLY == 3 ]]; then
					if [[ $(id -u) -eq 0 ]]; then
								echo "Home Space Utilization (All Users)"
								du -sh /home/*
					else
								echo "Home Space Utilization ($USER)"
								du -sh $HOME
					fi
					exit
			fi			
else
			echo "Invalid entry." >&2
			exit 1
fi

腳本在邏輯上分隔爲兩個部分。第一部分展示了菜單並獲取用戶的響應;第二部分對響應進行驗證並執行相應的選項。需要注意腳本中的exit命令的使用方法。在完成選定的功能後,exit可方式腳本繼續執行多餘的代碼。程序多出口通常並不是一個好現象(會導致程序邏輯難以理解),但是在上述腳本中適用。

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