在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ 讀取鍵盤輸入
網(wǎng)絡(luò)系統(tǒng)
打印
重定向
使用命令
位置參數(shù)
權(quán)限
文本處理
疑難排解
layout: book-zh title: 自定制 shell 提示符
查找文件
layout: book-zh title: vi 簡介
shell 環(huán)境
什么是 shell
編譯程序
鍵盤高級操作技巧
流程控制:case 分支
流程控制:if 分支結(jié)構(gòu)
layout: book-zh title: 軟件包管理
進程
存儲媒介
格式化輸出
編寫第一個 Shell 腳本
啟動一個項目
流程控制:while/until 循環(huán)
文件系統(tǒng)中跳轉(zhuǎn)
字符串和數(shù)字
讀取鍵盤輸入
歸檔和備份
探究操作系統(tǒng)
流程控制:for 循環(huán)
自頂向下設(shè)計
數(shù)組
操作文件和目錄
奇珍異寶
從 shell 眼中看世界
正則表達式

讀取鍵盤輸入

到目前為止我們編寫的腳本都缺乏一項在大多數(shù)計算機程序中都很常見的功能-交互性。也就是, 程序與用戶進行交互的能力。雖然許多程序不必是可交互的,但一些程序卻得到益處,能夠直接 接受用戶的輸入。以這個前面章節(jié)中的腳本為例:

#!/bin/bash
# test-integer2: evaluate the value of an integer.
INT=-5
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
    exit 1
fi

每次我們想要改變 INT 數(shù)值的時候,我們必須編輯這個腳本。如果腳本能請求用戶輸入數(shù)值,那 么它會更加有用處。在這個腳本中,我們將看一下我們怎樣給程序增加交互性功能。

read - 從標準輸入讀取數(shù)值

這個 read 內(nèi)部命令被用來從標準輸入讀取單行數(shù)據(jù)。這個命令可以用來讀取鍵盤輸入,當使用 重定向的時候,讀取文件中的一行數(shù)據(jù)。這個命令有以下語法形式:

read [-options] [variable...]

這里的 options 是下面列出的可用選項中的一個或多個,且 variable 是用來存儲輸入數(shù)值的一個或多個變量名。 如果沒有提供變量名,shell 變量 REPLY 會包含數(shù)據(jù)行。

基本上,read 會把來自標準輸入的字段賦值給具體的變量。如果我們修改我們的整數(shù)求值腳本,讓其使用 read ,它可能看起來像這樣:

#!/bin/bash
# read-integer: evaluate the value of an 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 "Input value is not an integer." >&2
    exit 1
fi

我們使用帶有 -n 選項(其會刪除輸出結(jié)果末尾的換行符)的 echo 命令,來顯示提示信息, 然后使用 read 來讀入變量 int 的數(shù)值。運行這個腳本得到以下輸出:

[me@linuxbox ~]$ read-integer
Please enter an integer -> 5
5 is positive.
5 is odd.

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 "var2 = '$var2'"
echo "var3 = '$var3'"
echo "var4 = '$var4'"
echo "var5 = '$var5'"

在這個腳本中,我們給五個變量賦值并顯示其結(jié)果。注意當給定不同個數(shù)的數(shù)值后,read 怎樣操作:

[me@linuxbox ~]$ read-multiple
Enter one or more values > a b c d e
var1 = 'a'
var2 = 'b'
var3 = 'c'
var4 = 'd'
var5 = 'e'
[me@linuxbox ~]$ read-multiple
Enter one or more values > a
var1 = 'a'
var2 = ''
var3 = ''
var4 = ''
var5 = ''
[me@linuxbox ~]$ read-multiple
Enter one or more values > a b c d e f g
var1 = 'a'
var2 = 'b'
var3 = 'c'
var4 = 'd'
var5 = 'e f g'

如果 read 命令接受到變量值數(shù)目少于期望的數(shù)字,那么額外的變量值為空,而多余的輸入數(shù)據(jù)則會 被包含到最后一個變量中。如果 read 命令之后沒有列出變量名,則一個 shell 變量,REPLY,將會包含 所有的輸入:

#!/bin/bash
# read-single: read multiple values into default variable
echo -n "Enter one or more values > "
read
echo "REPLY = '$REPLY'"

這個腳本的輸出結(jié)果是:

[me@linuxbox ~]$ read-single
Enter one or more values > a b c d
REPLY = 'a b c d'

選項

read 支持以下選送:

表29-1: read 選項
選項 說明
-a array 把輸入賦值到數(shù)組 array 中,從索引號零開始。我們 將在第36章中討論數(shù)組問題。
-d delimiter 用字符串 delimiter 中的第一個字符指示輸入結(jié)束,而不是一個換行符。
-e 使用 Readline 來處理輸入。這使得與命令行相同的方式編輯輸入。
-n num 讀取 num 個輸入字符,而不是整行。
-p prompt 為輸入顯示提示信息,使用字符串 prompt。
-r Raw mode. 不把反斜杠字符解釋為轉(zhuǎn)義字符。
-s Silent mode. 不會在屏幕上顯示輸入的字符。當輸入密碼和其它確認信息的時候,這會很有幫助。
-t seconds 超時. 幾秒鐘后終止輸入。read 會返回一個非零退出狀態(tài),若輸入超時。
-u fd 使用文件描述符 fd 中的輸入,而不是標準輸入。

使用各種各樣的選項,我們能用 read 完成有趣的事情。例如,通過-p 選項,我們能夠提供提示信息:

#!/bin/bash
# read-single: read multiple values into default variable
read -p "Enter one or more values > "
echo "REPLY = '$REPLY'"

通過 -t 和 -s 選項,我們可以編寫一個這樣的腳本,讀取“秘密”輸入,并且如果在特定的時間內(nèi) 輸入沒有完成,就終止輸入。

#!/bin/bash
# read-secret: input a secret pass phrase
if read -t 10 -sp "Enter secret pass phrase > " secret_pass; then
    echo -e "\nSecret pass phrase = '$secret_pass'"
else
    echo -e "\nInput timed out" >&2
    exit 1
if

這個腳本提示用戶輸入一個密碼,并等待輸入10秒鐘。如果在特定的時間內(nèi)沒有完成輸入, 則腳本會退出并返回一個錯誤。因為包含了一個 -s 選項,所以輸入的密碼不會出現(xiàn)在屏幕上。

IFS

通常,shell 對提供給 read 的輸入按照單詞進行分離。正如我們所見到的,這意味著多個由一個或幾個空格 分離開的單詞在輸入行中變成獨立的個體,并被 read 賦值給單獨的變量。這種行為由 shell 變量IFS (內(nèi)部字符分隔符)配置。IFS 的默認值包含一個空格,一個 tab,和一個換行符,每一個都會把 字段分割開。

我們可以調(diào)整 IFS 的值來控制輸入字段的分離。例如,這個 /etc/passwd 文件包含的數(shù)據(jù)行 使用冒號作為字段分隔符。通過把 IFS 的值更改為單個冒號,我們可以使用 read 讀取 /etc/passwd 中的內(nèi)容,并成功地把字段分給不同的變量。這個就是做這樣的事情:

#!/bin/bash
# read-ifs: read fields from a file
FILE=/etc/passwd
read -p "Enter a user name > " 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

這個腳本提示用戶輸入系統(tǒng)中一個帳戶的用戶名,然后顯示在文件 /etc/passwd/ 文件中關(guān)于用戶記錄的 不同字段。這個腳本包含兩個有趣的文本行。 第一個是:

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

這一行把 grep 命令的輸入結(jié)果賦值給變量 file_info。grep 命令使用的正則表達式 確保用戶名只會在 /etc/passwd 文件中匹配一個文本行。

第二個有意思的文本行是:

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

這一行由三部分組成:一個變量賦值,一個帶有一串參數(shù)的 read 命令,和一個奇怪的新的重定向操作符。 我們首先看一下變量賦值。

Shell 允許在一個命令之前立即發(fā)生一個或多個變量賦值。這些賦值為跟隨著的命令更改環(huán)境變量。 這個賦值的影響是暫時的;只是在命令存在期間改變環(huán)境變量。在這種情況下,IFS 的值改為一個冒號。 另外,我們也可以這樣編碼:

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

我們先存儲 IFS 的值,然后賦給一個新值,再執(zhí)行 read 命令,最后把 IFS 恢復原值。顯然,完成相同的任務, 在命令之前放置變量名賦值是一種更簡明的方式。

這個 <<< 操作符指示一個 here 字符串。一個 here 字符串就像一個 here 文檔,只是比較簡短,由 單個字符串組成。在這個例子中,來自 /etc/passwd 文件的數(shù)據(jù)發(fā)送給 read 命令的標準輸入。 我們可能想知道為什么選擇這種相當晦澀的方法而不是:

echo "$file_info" | IFS=":" read user pw uid gid name home shell

你不能管道 read

雖然通常 read 命令接受標準輸入,但是你不能這樣做:

echo "foo" | read

我們期望這個命令能生效,但是它不能。這個命令將顯示成功,但是 REPLY 變量 總是為空。為什么會這樣?

答案與 shell 處理管道線的方式有關(guān)系。在 bash(和其它 shells,例如 sh)中,管道線 會創(chuàng)建子 shell。它們是 shell 的副本,且用來執(zhí)行命令的環(huán)境變量在管道線中。 上面示例中,read 命令將在子 shell 中執(zhí)行。

在類 Unix 的系統(tǒng)中,子 shell 執(zhí)行的時候,會為進程創(chuàng)建父環(huán)境的副本。當進程結(jié)束 之后,環(huán)境副本就會被破壞掉。這意味著一個子 shell 永遠不能改變父進程的環(huán)境。read 賦值變量, 然后會變?yōu)榄h(huán)境的一部分。在上面的例子中,read 在它的子 shell 環(huán)境中,把 foo 賦值給變量 REPLY, 但是當命令退出后,子 shell 和它的環(huán)境將被破壞掉,這樣賦值的影響就會消失。

使用 here 字符串是解決此問題的一種方法。另一種方法將在37章中討論。

校正輸入

從鍵盤輸入這種新技能,帶來了額外的編程挑戰(zhàn),校正輸入。很多時候,一個良好編寫的程序與 一個拙劣程序之間的區(qū)別就是程序處理意外的能力。通常,意外會以錯誤輸入的形式出現(xiàn)。在前面 章節(jié)中的計算程序,我們已經(jīng)這樣做了一點兒,我們檢查整數(shù)值,甄別空值和非數(shù)字字符。每次 程序接受輸入的時候,執(zhí)行這類的程序檢查非常重要,為的是避免無效數(shù)據(jù)。對于 由多個用戶共享的程序,這個尤為重要。如果一個程序只使用一次且只被作者用來執(zhí)行一些特殊任務, 那么為了經(jīng)濟利益而忽略這些保護措施,可能會被原諒。即使這樣,如果程序執(zhí)行危險任務,比如說 刪除文件,所以最好包含數(shù)據(jù)校正,以防萬一。

這里我們有一個校正各種輸入的示例程序:

#!/bin/bash
# read-validate: validate input
invalid_input () {
    echo "Invalid input '$REPLY'" >&2
    exit 1
}
read -p "Enter a single item > "
# input is empty (invalid)
[[ -z $REPLY ]] && invalid_input
# input is multiple items (invalid)
(( $(echo $REPLY | wc -w) > 1 )) && invalid_input
# is input a valid filename?
if [[ $REPLY =~ ^[-[:alnum:]\._]+$ ]]; then
    echo "'$REPLY' is a 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

這個腳本提示用戶輸入一個數(shù)字。隨后,分析這個數(shù)字來決定它的內(nèi)容。正如我們所看到的,這個腳本 使用了許多我們已經(jīng)討論過的概念,包括 shell 函數(shù),[[ ]](( )),控制操作符 &&,以及 if 和 一些正則表達式。

菜單

一種常見的交互類型稱為菜單驅(qū)動。在菜單驅(qū)動程序中,呈現(xiàn)給用戶一系列選擇,并要求用戶選擇一項。 例如,我們可以想象一個展示以下信息的程序:

Please Select:
1.Display System Information
2.Display Disk Space
3.Display Home Space Utilization
0.Quit
Enter selection [0-3] >

使用我們從編寫 sys_info_page 程序中所學到的知識,我們能夠構(gòu)建一個菜單驅(qū)動程序來執(zhí)行 上述菜單中的任務:

#!/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
    0. 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"
        uptime
        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

The presence of multiple `exit` points in a program is generally a bad idea (it makes

從邏輯上講,這個腳本被分為兩部分。第一部分顯示菜單和用戶輸入。第二部分確認用戶反饋,并執(zhí)行 選擇的行動。注意腳本中使用的 exit 命令。在這里,在一個行動執(zhí)行之后, exit 被用來阻止腳本執(zhí)行不必要的代碼。 通常在程序中出現(xiàn)多個 exit 代碼是一個壞想法(它使程序邏輯較難理解),但是它在這個腳本中起作用。

總結(jié)歸納

在這一章中,我們向著程序交互性邁出了第一步;允許用戶通過鍵盤向程序輸入數(shù)據(jù)。使用目前 已經(jīng)學過的技巧,有可能編寫許多有用的程序,比如說特定的計算程序和容易使用的命令行工具 前端。在下一章中,我們將繼續(xù)建立菜單驅(qū)動程序概念,讓它更完善。

友情提示

仔細研究本章中的程序,并對程序的邏輯結(jié)構(gòu)有一個完整的理解,這是非常重要的,因為即將到來的 程序會日益復雜。作為練習,用 test 命令而不是[[ ]]復合命令來重新編寫本章中的程序。 提示:使用 grep 命令來計算正則表達式及其退出狀態(tài)。這會是一個不錯的實踐。

拓展閱讀

上一篇:進程下一篇:編寫第一個 Shell 腳本