日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747


Linux技巧:使用bash read命令實現(xiàn)一個簡易shell(完整版)

 

在 linux 上面,可以使用 bash 的 read 內(nèi)置命令來讀取用戶輸入。

當(dāng)在 while 循環(huán)中不斷調(diào)用 read 命令,并打印一些提示字符,如 $、#、> 等,就可以不斷接收用戶輸入,并執(zhí)行一些自定義的命令,類似一個簡易的 shell。

下面主要是介紹 read 命令的常見用法,用來逐步實現(xiàn)一個簡易的 shell 效果。

read 命令介紹

在 bash 中,read 內(nèi)置命令可以讀取用戶的一行輸入,并對輸入內(nèi)容進行單詞拆分,依次賦值給指定的變量。

查看 help read 的說明如下:

read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

Read a line from the standard input and split it into fields.

 

Reads a single line from the standard input, or from file descriptor FD if the -u option is supplied. The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on, with any leftover words assigned to the last NAME.

 

Only the characters found in $IFS are recognized as word delimiters.

If no NAMEs are supplied, the line read is stored in the REPLY variable.

 

Exit Status:

The return code is zero, unless end-of-file is encountered, read times out (in which case it's greater than 128), a variable assignment error occurs, or an invalid file descriptor is supplied as the argument to -u.

即,read 命令從標(biāo)準(zhǔn)輸入讀取到一行,每行內(nèi)容以換行符結(jié)尾,但是讀取到的內(nèi)容不包含行末的換行符。

對于讀取到的內(nèi)容,會按照 bash 的 IFS 全局變量保存的分割字符把輸入行拆分成多個單詞,把這些詞依次賦值給提供的變量,如果所給的變量個數(shù)少于分割后的單詞數(shù),最后一個變量被賦值為剩余的所有單詞。

舉例說明如下:

$ read first second third last
1 2 3 4 5 678
$ echo $first, $second, $third, $last
1, 2, 3, 4 5 678
$ read input_line
This is a test input line.
$ echo $input_line
This is a test input line.

可以看到,默認(rèn)基于空格來拆分單詞。

所給的第一個 first 變量被賦值為拆分后的第一個單詞。

第二個 second 變量被賦值為拆分后的第二個單詞。

第三個 third 變量被賦值為拆分后的第三個單詞。

最后一個 last 變量被賦值為第三個單詞后的所有單詞。

顯然,只提供一個變量時,整個輸入行都會賦值給這個變量,打印的 input_line 變量值可以看到這一點。

使用 -p 選項指定提示字符串

執(zhí)行 read 命令時,默認(rèn)不打印提示字符串。

如果想要引導(dǎo)用戶輸入特定的內(nèi)容,可以使用 -p 選項來指定提示字符串。

查看 help read 對該選項的說明如下:

-p prompt

output the string PROMPT without a trailing newline before attempting to read

即,在 -p 選項后面跟著一個 prompt 字符串。在讀取用戶輸入之前,會先打印這個 prompt 字符串,以作提示。

這個提示字符串后面不會換行,會跟用戶的輸入在同一行。

具體舉例說明如下:

$ read -p "Please input your mood today: " mood
Please input your mood today: hAppy
$ echo $mood
happy

在執(zhí)行所給的 read 命令時,會先打印 “Please input your mood today: ”字符串,沒有換行,等待用戶輸入。

上面的 “happy” 是輸入的字符串,會被賦值給指定 mood 變量。

當(dāng)使用 while 循環(huán)不斷調(diào)用 read 命令,且用 -p 選項指定 $ 字符時,看起來就像是一個簡易的 shell,可以根據(jù)用戶輸入做一些處理,也可以指定其他字符,如 >、# 等

假設(shè)有一個 tinyshell.sh 腳本,內(nèi)容如下:

#!/bin/bash

while read -p "tinyshell> " input; do
    if [ "$input" == "l" ]; then
        ls
    elif [ "$input" == "quit" ]; then
        break
    else
        echo "Unknown input: $input"
    fi
done

該腳本在 while 循環(huán)中不斷調(diào)用 read 命令,使用 -p 選項設(shè)置提示字符串為 “tinyshell> ”,DOS 命令行的提示字符就是 >。

具體執(zhí)行結(jié)果如下:

$ ./tinyshell.sh
tinyshell> l
tinyshell.sh
tinyshell> d
Unknown input: d
tinyshell> quit
$

在執(zhí)行時,先打印出 “tinyshell> ” 提示字符串,等待用戶輸入。

這里輸入 l 字符,腳本會執(zhí)行 ls 命令。

輸入 quit 字符串,會退出 while 循環(huán),終止執(zhí)行。

輸入其他內(nèi)容則提示 “Unknown input: ”。

在實際工作中,對這個例子進行擴展,就能模擬一個簡易的 shell 效果,可以輸入自定義的命令簡寫,來執(zhí)行一長串的命令,非常方便

例如進行 Android 系統(tǒng)開發(fā),經(jīng)常用到 adb shell 的各種命令,有些命令帶有很多參數(shù),比較難輸入,仿照這個例子,可以只輸入一個字符、或者幾個字符,然后執(zhí)行對應(yīng)的 adb shell 命令,減少很多輸入。

使用 -e 選項在交互式 shell 中獲取到歷史命令

前面提到在 while 循環(huán)中不斷執(zhí)行 read -p 命令,可以模擬一個簡易的 shell 效果。

實際使用時遇到一個問題,那就是輸入上光標(biāo)鍵,會打印 ^[[A,輸入下光標(biāo)鍵,會打印 ^[[B,不能像 bash 那樣通過上下光標(biāo)鍵顯示執(zhí)行過的歷史命令。

具體執(zhí)行結(jié)果如下:

$ ./tinyshell.sh
tinyshell> ^[[A^[[B

這里打印的 ^[[A 是輸入上光標(biāo)鍵所顯示,^[[B 是輸入下光標(biāo)鍵所顯示。

如果想要在執(zhí)行 read 命令時,可以通過上下光標(biāo)鍵來顯示歷史命令,需要加上 -e 選項,且在交互式 shell 中運行。

查看 help read 對 -e 選項說明如下:

-e

use Readline to obtain the line in an interactive shell

即,在交互式 shell 中,read -e 會使用 readline 庫來獲取輸入。

readline 庫支持很多強大的功能,上下光標(biāo)鍵能夠顯示歷史命令,就是因為默認(rèn)把上下光標(biāo)鍵綁定到 readline 庫獲取上下歷史命令的函數(shù)。

可以執(zhí)行下面的命令來進行確認(rèn):

$ bind -p | grep -E "previous-history|next-history"
"C-n": next-history
"eOB": next-history
"e[B": next-history
"C-p": previous-history
"eOA": previous-history
"e[A": previous-history

這里的 "e[A" 就是對應(yīng)上光標(biāo)鍵,綁定到 previous-history 功能,也就是顯示上一個歷史命令。

"e[B" 對應(yīng)下光標(biāo)鍵,綁定到 next_history 功能,也就是顯示下一個歷史命令。

上面的 "C-p" 對應(yīng) CTRL-p,也就是同時按下 CTRL 鍵和 p 鍵,可以看到它也對應(yīng)上一個歷史命令。

"C-n" 對應(yīng) CTRL-n,對應(yīng)下一個歷史命令。

一般來說,直接在 bash shell 中執(zhí)行 read 命令,就處于交互式 shell(interactive shell)之下。

具體舉例說明如下:

$ read
^[[A^[[B
$ read -e
read -e

這個例子先是直接執(zhí)行 read 命令,然后輸入上光標(biāo)鍵,會打印 ^[[A,然后輸入下光標(biāo)鍵,又打印 ^[[B。

之后,執(zhí)行 read -e 命令,輸入上光標(biāo)鍵,會自動填充上一個歷史命令,也就是正在執(zhí)行的 “read -e” 命令。

注意:這個 -e 選項只在交互式 shell 中才會生效。一般來說,shell 腳本是在非交互式 shell 中執(zhí)行。

當(dāng)在 shell 腳本中使用 read -e 時,輸入上下光標(biāo)鍵,不會再打印 ^[[A、^[[B,也不會顯示歷史命令,而是什么都沒有打印。

這跟 read -p 的效果有所不同,read -p 可以在輸入上下光標(biāo)鍵時,打印出 ^[[A、^[[B。

讓 shell 腳本運行在交互模式下

我們可以使用下面幾個方法來讓 shell 腳本在交互模式下執(zhí)行。

通過 bash -i 選項指定運行在交互模式下

在 bash 中,可以使用 bash 的 -i 選項來讓 shell 腳本在交互模式下運行。

查看 man bash 對 -i 選項說明如下:

-i

If the -i option is present, the shell is interactive.

即,在 shell 腳本開頭,把腳本的解釋器寫為 #/bin/bash -i。執(zhí)行這個 shell 腳本時,就會運行在交互模式下。

把前面的 tinyshell.sh 腳本修改成下面的內(nèi)容來進行驗證:

#!/bin/bash -i

while read -ep "tinyshell> " input; do
    if [ "$input" == "l" ]; then
        ls
    elif [ "$input" == "quit" ]; then
        break
    else
        echo "Unknown input: $input"
   fi
done

相比于之前的腳本,這次的改動點是:

  • 把之前的 #!/bin/bash 改成 #!/bin/bash -i,添加 -i 選項指定運行在交互模式下。
  • 把 read -p 改成 read -ep,添加 -e 選項指定在交互模式下用 readline 庫讀取用戶輸入。

執(zhí)行修改后的腳本,結(jié)果如下:

$ ./tinyshell.sh
tinyshell> #!/bin/bash -i
Unknown input: #!/bin/bash -i
tinyshell>

上面的在 “tinyshell>” 之后顯示的 “#!/bin/bash -i” 是輸入兩次上光標(biāo)鍵后顯示出來的歷史命令。第一個輸入光標(biāo)鍵會顯示腳本里面的整個 while 循環(huán)語句。

注意:這個腳本在 Linux Debian 系統(tǒng)、 Linux Ubuntu 系統(tǒng)本地測試都能生效,可以通過上下光標(biāo)鍵顯示出歷史命令。

但是在 windows 下通過 ssh 遠程登錄到 Ubuntu 系統(tǒng),在遠程 Ununtu 系統(tǒng)下執(zhí)行這個腳本不生效,即使把腳本開頭的解釋器寫為 #!/bin/bash -i,read -e 命令也無法通過上下光標(biāo)鍵讀取到歷史命令,輸入上下光標(biāo)鍵,什么都沒有打印出來。

在 mac OSX 系統(tǒng)下測試也不生效。

這幾種情況都是在 login shell 下運行,查看 readline 庫的配置文件也沒有看到異常,目前原因不明。

可以改成用 source 命令執(zhí)行腳本來避免這個異常。具體如后面說明所示。

通過 source 命令執(zhí)行 shell 腳本

通過 bash 的 source 內(nèi)置命令執(zhí)行 shell 腳本時,這個腳本運行在當(dāng)前 bash shell 下,而不是啟動一個子 shell 來執(zhí)行腳本。

由于當(dāng)前 bash shell 是交互式,運行在該 bash shell 下的腳本也是交互式。

此時,腳本開頭的解釋器不需要加 -i 選項,但 read 命令還是要加 -e 選項來指定用 readline 庫讀取輸入。

修改 tinyshell.sh 腳本內(nèi)容如下:

#!/bin/bash

while read -ep "tinyshell> " input; do
    if [ "$input" == "l" ]; then
        ls
    elif [ "$input" == "quit" ]; then
        break
    else
        bash -c "${input}"
    fi
done

這個腳本的改動點是:

  • 腳本開頭的解釋器寫為 #!/bin/bash,不需要加 -i 選項。
  • read 命令加了 -e 選項。
  • 對于不識別的輸入,使用 bash -c 來執(zhí)行所輸入的內(nèi)容,這樣就可以執(zhí)行外部的命令。

使用 source 命令執(zhí)行這個腳本的結(jié)果如下:

$ source tinyshell.sh
tinyshell> l
tinyshell.sh
tinyshell> echo "This is a tinyshell."
This is a tinyshell.
tinyshell> source tinyshell.sh
tinyshell> quit
tinyshell> quit
$

在執(zhí)行的時候,先是手動輸入 l 字符,該腳本會相應(yīng)執(zhí)行 ls 命令。

然后手動輸入 echo "This is a tinyshell.",該腳本使用 bash -c 來執(zhí)行這個命令,打印出 This is a tinyshell.。

接著輸入上光標(biāo)鍵,出現(xiàn)上一個歷史命令,顯示當(dāng)前正在執(zhí)行的 source tinyshell.sh 命令。

回車之后會再次執(zhí)行這個腳本。

可以看到,需要手動輸入兩次 quit,才退出這兩次執(zhí)行。

上面提到,在 Windows 下通過 ssh 遠程登錄到 Ubuntu 系統(tǒng),在遠程 Ununtu 系統(tǒng)下,使用 bash 的 -i 選項來執(zhí)行腳本,read -e 也不能通過上下光標(biāo)鍵來獲取歷史命令。

此時,通過 source 命令執(zhí)行腳本,read -e 命令能通過上下光標(biāo)鍵來獲取歷史命令。

即,通過 bash 的 -i 選項來執(zhí)行腳本,可能會受到子 shell 環(huán)境配置的影響,導(dǎo)致 read -e 命令不能通過上下光標(biāo)鍵來獲取歷史。

而通過 source 命令來執(zhí)行腳本,直接運行在當(dāng)前 bash shell 下,可以避免子 shell 環(huán)境配置的影響,兼容性較強。

注意:通過 source 命令執(zhí)行腳本時,腳本內(nèi)不能執(zhí)行 exit 命令,否則不但會退出腳本執(zhí)行,還會退出所在的 bash shell。

把腳本自身執(zhí)行的命令添加到當(dāng)前歷史記錄

在前面的腳本代碼中,無論是通過 bash 的 -i 選項來執(zhí)行腳本,還是通過 source 命令來執(zhí)行腳本,這兩種方式有一個共同的問題:雖然可以使用上下光標(biāo)鍵查找歷史命令,但找不到腳本自身所執(zhí)行的命令。

例如輸入 l 字符,tinyshell.sh 腳本執(zhí)行了 ls 命令。

通過上光標(biāo)鍵還是只能查找到執(zhí)行腳本之前的歷史命令,查找不到輸入的 l 字符,也找不到腳本所執(zhí)行的 ls 命令,就像是這個腳本的命令沒有加入到歷史記錄。

如果想在執(zhí)行腳本時,可以使用上下光標(biāo)鍵查找到腳本自身執(zhí)行的命令,可以使用 history -s 命令。

在上面 while 循環(huán)的末尾添加下面的語句,新增的代碼前面用 + 來標(biāo)識:

    else
        bash -c "${input}"
    fi
+   history -s "${input}"
done

添加 history -s "${input}" 語句后,就能通過上下光標(biāo)鍵找到 input 變量指定的命令。

例如輸入 l 字符,tinyshell.sh 腳本執(zhí)行了 ls 命令。

而 input 變量保存的是 l 字符,能夠通過上下光標(biāo)鍵找到 l 命令,找不到 ls 命令。

查看 man bash 對 history 內(nèi)置命令的 -s 選項說明如下:

history -s arg [arg ...]

-s: Store the args in the history list as a single entry.

即,history -s 命令把所給的參數(shù)添加到當(dāng)前歷史記錄中。

后續(xù)通過上下光標(biāo)鍵獲取歷史命令,就可以獲取到新添加的命令。

從文件中逐行讀取命令并執(zhí)行

既然是模擬一個簡易的 shell 效果,當(dāng)然要具有執(zhí)行腳本文件的能力。

我們可以通過重定向用 read 命令逐行讀取文件內(nèi)容,然后執(zhí)行每一行的命令。一段示例代碼如下:

while read line; do
    echo $line
done < filename

這段代碼會逐行讀取 fliename 這個文件的內(nèi)容,讀取到最后一行 (EOF) 就會退出 while 循環(huán)。

參考這段代碼,對 tinyshell.sh 腳本修改如下:

#!/bin/bash -i

if [ $# -ne 0 ]; then
    filename="$1"
else
    filename="/dev/stdin"
fi

while read -ep "tinyshell> " input; do
    if [ "$input" == "l" ]; then
        ls
    elif [ "$input" == "quit" ]; then
        break
    else
        bash -c "$input"
    fi
done < "$filename"

這個腳本使用 $# 獲取到傳入腳本的參數(shù)個數(shù)。

如果參數(shù)個數(shù)不等于 0,那么用 $1 獲取到第一個參數(shù)值,賦值給 filename 變量。

這個參數(shù)值用于指定要執(zhí)行的腳本文件名。

如果沒有提供任何參數(shù),那么將 filename 賦值為 /dev/stdin,對應(yīng)標(biāo)準(zhǔn)輸入。

注意不能將 filename 賦值為空字符串,否則重定向會提示文件找不到。

重定向空字符串并不表示獲取標(biāo)準(zhǔn)輸入。

為了避免所給文件名帶有空格導(dǎo)致異常,要用雙引號把 $filename 括起來。

這里采用 bash 的 -i 選項來執(zhí)行該腳本,所以要在 Linux 本地系統(tǒng)進行測試。

如果想要用 source 命令來執(zhí)行,需要做一些修改,包括調(diào)整 $#、$1 的使用。

這里不再提供使用 source 命令來執(zhí)行的例子。

執(zhí)行修改后的腳本,結(jié)果如下:

$ ./tinyshell.sh
tinyshell> l
shfile  tinyshell.sh
tinyshell> quit
$ cat shfile
l
echo "This is in a test file."
whoami
$ ./tinyshell.sh shfile
shfile  tinyshell.sh
This is in a test file.
shy

這個例子先執(zhí)行 ./tinyshell.sh 命令,不帶參數(shù)時,腳本指定從 /dev/stdin 獲取輸入,可以正常獲取到標(biāo)準(zhǔn)輸入。

輸入的是 l 字符,腳本執(zhí)行 ls 命令,列出當(dāng)前目錄下的文件,可以看到有一個 shfile 文件。

這個 shfile 文件就是要被執(zhí)行的腳本文件,用 cat shfile 命令列出它的內(nèi)容,只有三行,每一行都是要執(zhí)行的命令。

然后執(zhí)行 ./tinyshell.sh shfile 命令,從打印結(jié)果來看,確實逐行讀取到 shfile 文件的內(nèi)容,并執(zhí)行每一行的命令。

Bash 的 whoami 命令會打印當(dāng)前登錄的用戶名,這里打印出來是 shy。

即,使用修改后的 ./tinyshell.sh 來模擬 shell 效果,具有執(zhí)行腳本文件的能力。

雖然功能還很弱,但基本框架已經(jīng)搭好,后續(xù)可以根據(jù)實際需求進行擴展完善。

注意:使用上面的 “while read” 循環(huán)來逐行讀取文件內(nèi)容,有一個隱晦的異常:如果所給文件的最后一行不是以換行符結(jié)尾時,那么這個 “while read” 循環(huán)會處理不到最后一行。具體原因說明如下。

如果文件的最后一行以換行符結(jié)尾,那么 read 命令遇到換行符,會暫停獲取輸入,并把之前讀取到的內(nèi)容賦值給指定的變量,命令自身的返回值是 0。

之后 while 命令對這個值進行評估,0 對應(yīng) true,執(zhí)行循環(huán)里面的語句,處理最后一行的內(nèi)容。

然后再次執(zhí)行 read 命令,遇到文件結(jié)尾 (EOF),read 命令返回非 0 值,對應(yīng) false,退出 while 循環(huán)。這是正常的流程。

如果文件的最后一行不是以換行符結(jié)尾,read 讀取完這一行內(nèi)容,遇到了 EOF,會把讀取到的內(nèi)容賦值給指定的變量,命令自身返回值是非 0 值(使用 $? 獲取這個返回值,遇到 EOF 應(yīng)該是返回 1)。

之后 while 命令對這個非 0 值進行評估,就會退出 while 循環(huán),沒有執(zhí)行循環(huán)里面的語句。

即,這種情況下,雖然 read 命令還是會把最后一行內(nèi)容賦值給指定變量,但是退出了 while 循環(huán),沒有執(zhí)行循環(huán)里面的語句,沒有機會處理這一行的內(nèi)容。

除非在 while 循環(huán)外面再處理一次,但會造成代碼冗余。

下面修改 shfile 文件的內(nèi)容,最后一行不以換行符結(jié)尾,然后執(zhí)行 ./tinyshell.sh shfile 命令,結(jié)果如下:

$ echo -ne "lnwhoami" > shfile
$ ./tinyshell.sh shfile
shfile  tinyshell.sh
$ cat shfile
l
whoami$

這里使用 echo 命令的 -n 選項指定不在行末追加換行符,那么寫入文件的最后一行不以換行符結(jié)尾。

可以看到,執(zhí)行 ./tinyshell.sh shfile 命令,只處理了第一行的 l 字符,第二行的 whoami 沒有被執(zhí)行。

用 cat shfile 命令查看該文件內(nèi)容,whoami 跟命令行提示符打印在同一行,確實不以換行符結(jié)尾。

為了避免這個問題,可以在腳本中添加判斷,如果所給文件的最后一行不以換行符結(jié)尾,則追加一個換行符到文件末尾

要添加的代碼如下,新增的代碼前面用 + 來標(biāo)識:

if [ $# -ne 0 ]; then
    filename="$1"
+    if test -n "$(tail "$filename" -c 1)"; then
+        echo >> "$filename"
+    fi
else
    filename="/dev/stdin"
fi

新增的代碼用 tail "$filename" -c 1 命令獲取到 filename 文件的最后一個字符。

"$(tail "$filename" -c 1)" 語句經(jīng)過命令擴展后返回這個字符。

如果這個字符是換行符,由于 bash 在擴展后會自動丟棄字符串的最后一個換行符,獲取到的內(nèi)容為空,test -n 返回為 false,不做處理。

如果最后一個字符不是換行符,那么內(nèi)容不為空,test -n 返回為 true,就會執(zhí)行 echo >> "$filename" 命令追加一個換行符到文件末尾。

echo 不帶參數(shù)時,默認(rèn)輸出一個換行符, >> 表示追加內(nèi)容到文件末尾。

添加這幾個語句后,再執(zhí)行 ./tinyshell.sh shfile 命令,就能處理到最后一行的 whoami,如下所示:

$ ./tinyshell.sh shfile
shfile  tinyshell.sh
shy
$ cat shfile
l
whoami
$

可以看到,執(zhí)行之后,shfile 文件的最后一行 whoami 后面被追加了一個換行符,輸出該文件內(nèi)容,命令行提示符會換行打印。

通常來說,在 Windows 下復(fù)制內(nèi)容到新建文件,然后保存這個文件,文件的最后一行可能就不以換行符結(jié)尾。

使用 -s 選項指定不回顯用戶輸入

在 bash 下,輸入密碼時,一般不會回顯用戶輸入,而是什么都不顯示。我們可以使用 read 命令的 -s 選項模擬這個效果。

查看 help read 對 -s 選項的說明如下:

-s

do not echo input coming from a terminal

具體舉例如下:

$ read -s -p "Your input will not echo: " input
Your input will not echo: $ echo $input
sure?

這個例子指定了 -s 選項,不回顯輸入內(nèi)容到終端。

用 -p 指定了提示字符串,輸入內(nèi)容會被保存到 input 變量。

在輸入的時候,界面上不會顯示任何字符。

回車之后,命令行提示符直接顯示在同一行,由于沒有回顯換行符,所以沒有換行。

打印 input 變量的值,可以看到手動輸入的內(nèi)容是 “sure?”。

如果需要在模擬的簡易 shell 中輸入密碼,可以添加類似下面的代碼,讓輸入密碼時不回顯,新增的代碼前面用 + 來標(biāo)識:

    elif [ "$input" == "quit" ]; then
        break
+    elif [ "$input" == "root" ]; then
+        read -s -p "Please input your password: " pwd
+        # handle password with $pwd
+        echo
+        echo "Your are root now."
    else
        bash -c "${input}"
    fi

這里添加了對 root 字符串的處理,先執(zhí)行 read -s -p "Please input your password: " pwd 命令,提示讓用戶輸入密碼。

輸入的內(nèi)容不會回顯,會保存在 pwd 變量中,可以根據(jù)實際需要進行處理。

新增的第一個 echo 命令用于從 "Please input your password: " 字符串后面換行,否則會直接輸出到同一行上。

第二個 echo 命令只是打印一個提示語,可以根據(jù)實際需求改成對應(yīng)的提示。

使用 -n 選項指定讀取多少個字符

執(zhí)行 read 命令讀取標(biāo)準(zhǔn)輸入,會不停讀取輸入內(nèi)容,直到遇到換行符為止。

如果我們預(yù)期最多只讀取幾個字符,可以使用 -n 選項來指定。

查看 help read 對 -n 選項說明如下:

-n nchars

return after reading NCHARS characters rather than waiting for a newline, but honor a delimiter if fewer than NCHARS characters are read before the delimiter

即,read -n nchars 指定最多只讀取 nchars 個字符。

輸入 nchars 個字符后,即使還沒有遇到換行符,read 也會停止讀取輸入,返回讀取到的內(nèi)容。

如果在輸入 nchar 個字符之前,就遇到換行符,也會停止讀取輸入。

使用 -n 選項并不表示一定要讀取到 nchars 個字符。

另外一個 -N 選項表示一定要讀取到 nchars 個字符。這里對 -N 選項不做說明。

下面會在模擬的簡易 shell 中實現(xiàn)一個小游戲,增加一點趣味性。

這個小游戲使用 read -n 1 來指定每次只讀取一個字符,以便輸入字符就立刻停止讀取,不需要再按回車。

具體實現(xiàn)代碼如下,這也是 tinyshell.sh 腳本最終版的代碼:

#!/bin/bash -i

if [ $# -ne 0 ]; then
    filename="$1"
    if test -n "$(tail "$filename" -c 1)"; then
        echo >> "$filename"
    fi
else
    filename="/dev/stdin"
fi

function game()
{
    local count=0
    local T="T->"

    echo -e "NOW, ATTACK! $T"
    while read -s -n 1 char; do
        case $char in
            "h") ((--count)) ;;
            "l") ((++count)) ;;
            "q") break ;;
        esac

        for ((i = 0; i < count; ++i)); do
            echo -n "    "
        done
        echo -ne "$T      r"
    done
    echo
}

while read -ep "tinyshell> " input; do
    if [ "$input" == "l" ]; then
        ls
    elif [ "$input" == "quit" ]; then
        break
    elif [ "$input" == "root" ]; then
        read -s -p "Please input your password: " pwd
        # handle with $pwd
        echo
        echo "Your are root now."
    elif [ "$input" == "game" ]; then
        game
    else
        bash -c "${input}"
    fi
    history -s "${input}"
done < "$filename"

主要改動是增加對 game 字符串的處理,輸入這個字符串,會執(zhí)行自定義的 game 函數(shù)。

該函數(shù)打印 T-> 字符串,像是一把劍(也許吧),然后用 read -s -n 1 char 命令指定每次只讀取一個字符,且不回顯。

如果輸入 l 字符,則把 T-> 字符串的顯示位置往右移。

輸入 h 字符,則把 T-> 字符串的顯示位置往左移。

看起來是一個左右移動的效果。

輸入 q 字符,退出該游戲。

具體執(zhí)行結(jié)果如下:

$ ./tinyshell.sh
tinyshell> game
NOW, ATTACK! T->
        T->
tinyshell>

由于沒有回顯輸入字符,且始終在同一行顯示 T-> 字符串,所以這個打印結(jié)果體現(xiàn)不出 T-> 字符串的移動,可以實際執(zhí)行這個腳本,多次輸入 l 、h 字符,就能看到具體效果,最后輸入 q 字符退出游戲。

總結(jié)

至此,我們已經(jīng)使用 read 命令來獲取用戶輸入,模擬了一個簡易的 shell 效果。

這個簡易的 shell 可以執(zhí)行腳本文件,可以通過上下光標(biāo)鍵獲取到 bash 的歷史命令,支持輸入密碼不回顯,還實現(xiàn)了一個小游戲。

總結(jié) read 命令的使用關(guān)鍵點如下:

  • 使用 -p 選項來打印提示字符,模擬 shell 的命令行提示符
  • 使用 -e 選項在交互式 shell 中用 readline 庫讀取輸入,可以避免輸入上下光標(biāo)鍵顯示亂碼
  • 使用 -s 選項指定不回顯輸入內(nèi)容,可用于輸入密碼、輸入游戲控制按鍵等情況
  • 使用 -n 1 選項指定只讀取一個字符,輸入字符立刻結(jié)束讀取,可以在游戲中快速響應(yīng)按鍵,不用按下回車才能響應(yīng)
  • 使用 “while read” 循環(huán)來重定向讀取文件,可以逐行讀取文件內(nèi)容,執(zhí)行相應(yīng)命令,就像是執(zhí)行腳本文件

分享到:
標(biāo)簽:bash read
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定