<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                現在我們的程序還缺少一種本領,就是接收和處理命令行選項和參數的能力。在這一章中,我們將探究一些能 讓程序訪問命令行內容的 shell 性能。 ## 訪問命令行 shell 提供了一個稱為位置參數的變量集合,這個集合包含了命令行中所有獨立的單詞。這些變量按照從0到9給予命名。 可以以這種方式講明白: ~~~ #!/bin/bash # posit-param: script to view command line parameters echo " \$0 = $0 \$1 = $1 \$2 = $2 \$3 = $3 \$4 = $4 \$5 = $5 \$6 = $6 \$7 = $7 \$8 = $8 \$9 = $9 " ~~~ 一個非常簡單的腳本,顯示從 $0 到 $9 所有變量的值。當不帶命令行參數執行該腳本時,輸出結果如下: ~~~ [me@linuxbox ~]$ posit-param $0 = /home/me/bin/posit-param $1 = $2 = $3 = $4 = $5 = $6 = $7 = $8 = $9 = ~~~ 即使不帶命令行參數,位置參數 $0 總會包含命令行中出現的第一個單詞,也就是已執行程序的路徑名。 當帶參數執行腳本時,我們看看輸出結果: ~~~ [me@linuxbox ~]$ posit-param a b c d $0 = /home/me/bin/posit-param $1 = a $2 = b $3 = c $4 = d $5 = $6 = $7 = $8 = $9 = ~~~ 注意: 實際上通過參數展開方式你可以訪問的參數個數多于9個。只要指定一個大于9的數字,用花括號把該數字括起來就可以。 例如 ${10}, ${55}, ${211},等等。 ### 確定參數個數 另外 shell 還提供了一個名為 $#,可以得到命令行參數個數的變量: ~~~ #!/bin/bash # posit-param: script to view command line parameters echo " Number of arguments: $# \$0 = $0 \$1 = $1 \$2 = $2 \$3 = $3 \$4 = $4 \$5 = $5 \$6 = $6 \$7 = $7 \$8 = $8 \$9 = $9 " ~~~ 結果是: ~~~ [me@linuxbox ~]$ posit-param a b c d Number of arguments: 4 $0 = /home/me/bin/posit-param $1 = a $2 = b $3 = c $4 = d $5 = $6 = $7 = $8 = $9 = ~~~ ### shift - 訪問多個參數的利器 但是如果我們給一個程序添加大量的命令行參數,會怎么樣呢? 正如下面的例子: ~~~ [me@linuxbox ~]$ posit-param * Number of arguments: 82 $0 = /home/me/bin/posit-param $1 = addresses.ldif $2 = bin $3 = bookmarks.html $4 = debian-500-i386-netinst.iso $5 = debian-500-i386-netinst.jigdo $6 = debian-500-i386-netinst.template $7 = debian-cd_info.tar.gz $8 = Desktop $9 = dirlist-bin.txt ~~~ 在這個例子運行的環境下,通配符 * 展開成82個參數。我們如何處理那么多的參數? 為此,shell 提供了一種方法,盡管笨拙,但可以解決這個問題。執行一次 shift 命令, 就會導致所有的位置參數 “向下移動一個位置”。事實上,用 shift 命令也可以 處理只有一個參數的情況(除了其值永遠不會改變的變量 $0): ~~~ #!/bin/bash # posit-param2: script to display all arguments count=1 while [[ $# -gt 0 ]]; do echo "Argument $count = $1" count=$((count + 1)) shift done ~~~ 每次 shift 命令執行的時候,變量 $2 的值會移動到變量 $1 中,變量 $3 的值會移動到變量 $2 中,依次類推。 變量 $# 的值也會相應的減1。 在該 posit-param2 程序中,我們編寫了一個計算剩余參數數量,只要參數個數不為零就會繼續執行的 while 循環。 我們顯示當前的位置參數,每次循環迭代變量 count 的值都會加1,用來計數處理的參數數量, 最后,執行 shift 命令加載 $1,其值為下一個位置參數的值。這里是程序運行后的輸出結果: ~~~ [me@linuxbox ~]$ posit-param2 a b c d Argument 1 = a Argument 2 = b Argument 3 = c Argument 4 = d ~~~ ### 簡單應用 即使沒有 shift 命令,也可以用位置參數編寫一個有用的應用。舉例說明,這里是一個簡單的輸出文件信息的程序: ~~~ #!/bin/bash # file_info: simple file information program PROGNAME=$(basename $0) if [[ -e $1 ]]; then echo -e "\nFile Type:" file $1 echo -e "\nFile Status:" stat $1 else echo "$PROGNAME: usage: $PROGNAME file" >&2 exit 1 fi ~~~ 這個程序顯示一個具體文件的文件類型(由 file 命令確定)和文件狀態(來自 stat 命令)。該程序一個有意思 的特點是 PROGNAME 變量。它的值就是 basename $0 命令的執行結果。這個 basename 命令清除 一個路徑名的開頭部分,只留下一個文件的基本名稱。在我們的程序中,basename 命令清除了包含在 $0 位置參數 中的路徑名的開頭部分,$0 中包含著我們示例程序的完整路徑名。當構建提示信息正如程序結尾的使用信息的時候, basename $0 的執行結果就很有用處。按照這種方式編碼,可以重命名該腳本,且程序信息會自動調整為 包含相應的程序名稱。 ### Shell 函數中使用位置參數 正如位置參數被用來給 shell 腳本傳遞參數一樣,它們也能夠被用來給 shell 函數傳遞參數。為了說明這一點, 我們將把 file_info 腳本轉變成一個 shell 函數: ~~~ file_info () { # file_info: function to display file information if [[ -e $1 ]]; then echo -e "\nFile Type:" file $1 echo -e "\nFile Status:" stat $1 else echo "$FUNCNAME: usage: $FUNCNAME file" >&2 return 1 fi } ~~~ 現在,如果一個包含 shell 函數 file_info 的腳本調用該函數,且帶有一個文件名參數,那這個參數會傳遞給 file_info 函數。 通過此功能,我們可以寫出許多有用的 shell 函數,這些函數不僅能在腳本中使用,也可以用在 .bashrc 文件中。 注意那個 PROGNAME 變量已經改成 shell 變量 FUNCNAME 了。shell 會自動更新 FUNCNAME 變量,以便 跟蹤當前執行的 shell 函數。注意位置參數 $0 總是包含命令行中第一項的完整路徑名(例如,該程序的名字), 但不會包含這個我們可能期望的 shell 函數的名字。 ## 處理集體位置參數 有時候把所有的位置參數作為一個集體來管理是很有用的。例如,我們可能想為另一個程序編寫一個 “包裹程序”。 這意味著我們會創建一個腳本或 shell 函數,來簡化另一個程序的執行。包裹程序提供了一個神秘的命令行選項 列表,然后把這個參數列表傳遞給下一級的程序。 為此 shell 提供了兩種特殊的參數。他們二者都能擴展成完整的位置參數列表,但以相當微妙的方式略有不同。它們是: 表 32-1: * 和 @ 特殊參數 | 參數 | 描述 | | $* | 展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候,展開成一個由雙引號引起來 的字符串,包含了所有的位置參數,每個位置參數由 shell 變量 IFS 的第一個字符(默認為一個空格)分隔開。 | | $@ | 展開成一個從1開始的位置參數列表。當它被用雙引號引起來的時候, 它把每一個位置參數展開成一個由雙引號引起來的分開的字符串。 | 下面這個腳本用程序中展示了這些特殊參數: ~~~ #!/bin/bash # posit-params3 : script to demonstrate $* and $@ print_params () { echo "\$1 = $1" echo "\$2 = $2" echo "\$3 = $3" echo "\$4 = $4" } pass_params () { echo -e "\n" '$* :'; print_params $* echo -e "\n" '"$*" :'; print_params "$*" echo -e "\n" '$@ :'; print_params $@ echo -e "\n" '"$@" :'; print_params "$@" } pass_params "word" "words with spaces" ~~~ 在這個相當復雜的程序中,我們創建了兩個參數: “word” 和 “words with spaces”,然后把它們 傳遞給 pass_params 函數。這個函數,依次,再把兩個參數傳遞給 print_params 函數, 使用了特殊參數 $* 和 $@ 提供的四種可用方法。腳本運行后,揭示了這兩個特殊參數存在的差異: ~~~ [me@linuxbox ~]$ posit-param3 $* : $1 = word $2 = words $3 = with $4 = spaces "$*" : $1 = word words with spaces $2 = $3 = $4 = $@ : $1 = word $2 = words $3 = with $4 = spaces "$@" : $1 = word $2 = words with spaces $3 = $4 = ~~~ 通過我們的參數,$* 和 $@ 兩個都產生了一個有四個詞的結果: ~~~ word words with spaces "$*" produces a one word result: "word words with spaces" "$@" produces a two word result: "word" "words with spaces" ~~~ 這個結果符合我們實際的期望。我們從中得到的教訓是盡管 shell 提供了四種不同的得到位置參數列表的方法, 但到目前為止, “$@” 在大多數情況下是最有用的方法,因為它保留了每一個位置參數的完整性。 ## 一個更復雜的應用 經過長時間的間斷,我們將恢復程序 sys_info_page 的工作。我們下一步要給程序添加如下幾個命令行選項: * **輸出文件**。 我們將添加一個選項,以便指定一個文件名,來包含程序的輸出結果。 選項格式要么是 -f file,要么是 --file file * **交互模式**。這個選項將提示用戶輸入一個輸出文件名,然后判斷是否指定的文件已經存在了。如果文件存在, 在覆蓋這個存在的文件之前會提示用戶。這個選項可以通過 -i 或者 --interactive 來指定。 * **幫助**。指定 -h 選項 或者是 --help 選項,可導致程序輸出提示性的使用信息。 這里是處理命令行選項所需的代碼: ~~~ usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } # process command line options interactive= filename= while [[ -n $1 ]]; do case $1 in -f | --file) shift filename=$1 ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done ~~~ 首先,我們添加了一個叫做 usage 的 shell 函數,以便顯示幫助信息,當啟用幫助選項或敲寫了一個未知選項的時候。 下一步,我們開始處理循環。當位置參數 $1 不為空的時候,這個循環會持續運行。在循環的底部,有一個 shift 命令, 用來提升位置參數,以便確保該循環最終會終止。在循環體內,我們使用了一個 case 語句來檢查當前位置參數的值, 看看它是否匹配某個支持的選項。若找到了匹配項,就會執行與之對應的代碼。若沒有,就會打印出程序使用信息, 該腳本終止且執行錯誤。 處理 -f 參數的方式很有意思。當監測到 -f 參數的時候,會執行一次 shift 命令,從而提升位置參數 $1 為 伴隨著 -f 選項的 filename 參數。 我們下一步添加代碼來實現交互模式: ~~~ # interactive mode if [[ -n $interactive ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e $filename ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case $REPLY in Y|y) break ;; Q|q) echo "Program terminated." exit ;; *) continue ;; esac elif [[ -z $filename ]]; then continue else break fi done fi ~~~ 若 interactive 變量不為空,就會啟動一個無休止的循環,該循環包含文件名提示和隨后存在的文件處理代碼。 如果所需要的輸出文件已經存在,則提示用戶覆蓋,選擇另一個文件名,或者退出程序。如果用戶選擇覆蓋一個 已經存在的文件,則會執行 break 命令終止循環。注意 case 語句是怎樣只檢測用戶選擇了覆蓋還是退出選項。 其它任何選擇都會導致循環繼續并提示用戶再次選擇。 為了實現這個輸出文件名的功能,首先我們必須把現有的這個寫頁面(page-writing)的代碼轉變成一個 shell 函數, 一會兒就會明白這樣做的原因: ~~~ write_html_page () { cat <<- _EOF_ <HTML> <HEAD> <TITLE>$TITLE</TITLE> </HEAD> <BODY> <H1>$TITLE</H1> <P>$TIMESTAMP</P> $(report_uptime) $(report_disk_space) $(report_home_space) </BODY> </HTML> _EOF_ return } # output html page if [[ -n $filename ]]; then if touch $filename && [[ -f $filename ]]; then write_html_page > $filename else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi ~~~ 解決 -f 選項邏輯的代碼出現在以上程序片段的末尾。在這段代碼中,我們測試一個文件名是否存在,若文件名存在, 則執行另一個測試看看該文件是不是可寫文件。為此,會運行 touch 命令,緊隨其后執行一個測試,來決定 touch 命令 創建的文件是否是個普通文件。這兩個測試考慮到了輸入是無效路徑名(touch 命令執行失敗),和一個普通文件已經存在的情況。 正如我們所看到的,程序調用 write_html_page 函數來生成實際的網頁。函數輸出要么直接定向到 標準輸出(若 filename 變量為空的話)要么重定向到具體的文件中。 ## 總結 伴隨著位置參數的加入,現在我們能編寫相當具有功能性的腳本。例如,重復性的任務,位置參數使得編寫 非常有用的,可以放置在一個用戶的 .bashrc 文件中的 shell 函數成為可能。 我們的 sys_info_page 程序日漸精進。這里是一個完整的程序清單,最新的更改用高亮顯示: ~~~ #!/bin/bash # sys_info_page: program to output a system information page PROGNAME=$(basename $0) TITLE="System Information Report For $HOSTNAME" CURRENT_TIME=$(date +"%x %r %Z") TIMESTAMP="Generated $CURRENT_TIME, by $USER" report_uptime () { cat <<- _EOF_ <H2>System Uptime</H2> <PRE>$(uptime)</PRE> _EOF_ return } report_disk_space () { cat <<- _EOF_ <H2>Disk Space Utilization</H2> <PRE>$(df -h)</PRE> _EOF_ return } report_home_space () { if [[ $(id -u) -eq 0 ]]; then cat <<- _EOF_ <H2>Home Space Utilization (All Users)</H2> <PRE>$(du -sh /home/*)</PRE> _EOF_ else cat <<- _EOF_ <H2>Home Space Utilization ($USER)</H2> <PRE>$(du -sh $HOME)</PRE> _EOF_ fi return } usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } write_html_page () { cat <<- _EOF_ <HTML> <HEAD> <TITLE>$TITLE</TITLE> </HEAD> <BODY> <H1>$TITLE</H1> <P>$TIMESTAMP</P> $(report_uptime) $(report_disk_space) $(report_home_space) </BODY> </HTML> _EOF_ return } # process command line options interactive= filename= while [[ -n $1 ]]; do case $1 in -f | --file) shift filename=$1 ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done # interactive mode if [[ -n $interactive ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e $filename ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case $REPLY in Y|y) break ;; Q|q) echo "Program terminated." exit ;; *) continue ;; esac fi done fi # output html page if [[ -n $filename ]]; then if touch $filename && [[ -f $filename ]]; then write_html_page > $filename else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi ~~~ 我們還沒有完成。仍然還有許多事情我們可以做,可以改進。 ## 拓展閱讀 * Bash Hackers Wiki?上有一篇不錯的關于位置參數的文章: [http://wiki.bash-hackers.org/scripting/posparams](http://wiki.bash-hackers.org/scripting/posparams) * Bash 的參考手冊有一篇關于特殊參數的文章,包括 $* 和 $@: [http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters](http://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters) * 除了本章討論的技術之外,bash 還包含一個叫做 getopts 的內部命令,此命令也可以用來處理命令行參數。 bash 參考頁面的 SHELL BUILTIN COMMANDS 一節介紹了這個命令,Bash Hackers Wiki 上也有對它的描述: [http://wiki.bash-hackers.org/howto/getopts_tutorial](http://wiki.bash-hackers.org/howto/getopts_tutorial)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看