如何使用 getopts 解析 Linux Shell 腳本選項

已發表: 2022-06-25
顯示終端文本的筆記本電腦屏幕。
fatmawati achmad zaenuri/Shutterstock.com

您是否希望您的 Linux shell 腳本能夠更優雅地處理命令行選項和參數? 內置的 Bash getopts可讓您巧妙地解析命令行選項,而且也很容易。 我們向您展示如何。

介紹內置的 getopts

傳遞給 Bash 腳本是一件非常簡單的事情。 您從命令行或另一個腳本調用您的腳本,並在腳本名稱後面提供您的值列表。 這些值可以在腳本中作為變量訪問,從第一個變量的$1開始,第二個變量的$2開始,依此類推。

但是,如果您想將選項傳遞給腳本,情況很快就會變得更加複雜。 當我們說選項時,我們指的是像ls這樣的程序可以處理的選項、標誌或開關。 它們前面有一個破折號“ - ”,通常作為程序的指示器來打開或關閉其功能的某些方面。

如何在 Linux 上使用 ls 命令列出文件和目錄
相關如何在 Linux 上使用 ls 命令列出文件和目錄

ls命令有超過 50 個選項,主要與格式化其輸出有關。 -X (按擴展名排序)選項按文件擴展名的字母順序對輸出進行排序。 -U (未排序)選項按目錄順序列出。

選項就是這樣——它們是可選的。 您不知道用戶將選擇使用哪些選項(如果有的話),您也不知道他們在命令行中列出它們的順序。 這增加了解析選項所需的代碼的複雜性。

如果您的某些選項帶有一個參數,稱為選項參數,事情會變得更加複雜,例如, ls -w (width) 選項後面需要一個數字,表示輸出的最大顯示寬度。 當然,您可能會將其他參數傳遞到腳本中,這些參數只是數據值,根本不是選項。

幸運的是getopts為您處理了這種複雜性。 而且因為它是內置的,它在所有具有 Bash shell 的系統上都可用,因此無需安裝任何東西。

注意:getopts 不是 getopt

有一個名為getopt的舊實用程序。 這是一個小型實用程序,不是內置的。 有許多不同版本的getopt具有不同的行為,而內置的getops遵循 POSIX 準則。

 輸入 getopts
 輸入 getopt 

使用 type 命令查看 getop 和 getops 的區別

因為getopt不是內置的,所以它不共享getopts的一些自動好處,例如明智地處理空格。 使用getopts ,Bash shell 正在運行您的腳本,而 Bash shell 正在執行選項解析。 您無需調用外部程序來處理解析。

權衡是getopts不處理雙破折號的長格式選項名稱。 因此,您可以使用-w格式的選項,但不能使用“ ---wide-format ”。 另一方面,如果您有一個接受選項-a-b-c的腳本, getopts可以讓您像-abc-bca-bac等那樣組合它們。

我們將在本文中討論和演示getopts ,因此請確保將最後的“s”添加到命令名稱中。

相關:如何在 Windows 命令行上轉義文件路徑中的空格

快速回顧:處理參數值

此腳本不使用-a-b等虛線選項。 它確實接受命令行上的“普通”參數,並且這些參數在腳本內部作為值訪問。

 #!/bin/bash

# 一一獲取變量
echo "變量一:$1" 
echo "變量二:$2" 
echo "變量三:$3"

# 循環遍歷變量
對於 " $@" 中的 var 做
迴聲“$ var” 
完畢

這些參數在腳本內部作為變量$1$2$3訪問。

將此文本複製到編輯器中並將其保存為名為“variables.sh”的文件。 我們需要使用chmod命令使其可執行。 您需要對我們討論的所有腳本執行此步驟。 每次只需替換相應腳本文件的名稱即可。

 chmod +x 變量.sh 

使用 chmod 命令使腳本可執行

如果我們在沒有參數的情況下運行我們的腳本,我們會得到這個輸出。

 ./variables.sh 

運行不帶參數的腳本

我們沒有傳遞任何參數,因此腳本沒有要報告的值。 這次我們提供一些參數。

 ./variables.sh 如何極客

運行一個以三個單詞作為參數的腳本

正如預期的那樣,變量$1$2$3已設置為參數值,我們看到這些打印出來了。

這種一對一的參數處理方式意味著我們需要提前知道會有多少個參數。 腳本底部的循環並不關心有多少參數,它總是循環遍歷它們。

如果我們提供第四個參數,它不會分配給變量,但循環仍然會處理它。

 ./variables.sh 如何訪問極客網站

將四個參數傳遞給只能處理三個的腳本

如果我們在其中兩個單詞周圍加上引號,它們將被視為一個參數。

 ./variables.sh 如何“極客” 

引用兩個命令行參數以將它們視為一個參數

如果我們需要我們的腳本來處理選項、帶參數的選項和“普通”數據類型參數的所有組合,我們需要將選項與常規參數分開。 我們可以通過將所有選項(帶或不帶參數)放在常規參數之前來實現。

但是,在我們能走路之前,我們不要跑。 讓我們看一下處理命令行選項的最簡單情況。

處理選項

我們在while循環中使用getopts 。 循環的每次迭代都對傳遞給腳本的一個選項起作用。 在每種情況下,變量OPTION都設置為由getopts標識的選項。

在循環的每次迭代中, getopts都會轉到下一個選項。 當沒有更多選項時, getopts返回false並退出while循環。

如何在 Bash 腳本中使用案例語句
相關如何在 Bash 腳本中使用案例語句

OPTION變量與每個 case 語句子句中的模式相匹配。 因為我們使用的是 case 語句,所以在命令行中提供選項的順序並不重要。 每個選項都被放入 case 語句中,並觸發相應的子句。

case 語句中的各個子句使在腳本中執行特定於選項的操作變得容易。 通常,在實際腳本中,您會在每個子句中設置一個變量,這些變量將在腳本中進一步充當標誌,允許或拒絕某些功能。

將此文本複製到編輯器中並將其保存為名為“options.sh”的腳本,並使其可執行。

 #!/bin/bash

而getopts'abc'選項; 做
  案例“$OPTION”在 
    一個) 
      echo "使用的選項" ;;

    b)
      echo "使用了選項 b"
      ;;

    C)
      echo "使用了選項 c"
      ;;

    ?) 
      echo "用法:$(basename $0) [-a] [-b] [-c]"
      1號出口
      ;;
  經社理事會
完畢

這是定義 while 循環的行。

 而getopts'abc'選項; 做

getopts命令後跟選項字符串。 這列出了我們將用作選項的字母。 只有此列表中的字母可用作選項。 所以在這種情況下, -d將是無效的。 這會被?)子句困住,因為getopts返回一個問號“ ? ” 對於一個未識別的選項。 如果發生這種情況,正確的用法將打印到終端窗口:

 echo "用法:$(basename $0) [-a] [-b] [-c]"

按照慣例,在這種類型的正確使用消息中將選項括在括號“ [] ”中意味著該選項是可選的。 basename 命令從文件名中刪除任何目錄路徑。 腳本文件名在 Bash 腳本中保存在$0中。

讓我們將此腳本與不同的命令行組合一起使用。

 ./options.sh -a
 ./options.sh -a -b -c
 ./options.sh -ab -c
 ./options.sh -cab 

測試可以接受開關類型命令行選項的腳本

正如我們所見,我們所有的選項測試組合都被正確解析和處理。 如果我們嘗試一個不存在的選項怎麼辦?

 ./options.sh -d 

shell 和腳本報告了一個無法識別的選項

觸發了用法子句,這很好,但我們也從 shell 中得到了錯誤消息。 這對您的用例可能或可能無關緊要。 如果您從另一個必須解析錯誤消息的腳本調用腳本​​,那麼如果 shell 也生成錯誤消息,這將變得更加困難。

關閉 shell 錯誤消息非常容易。 我們需要做的就是將冒號“ : ”作為選項字符串的第一個字符。

編輯您的“options.sh”文件並添加一個冒號作為選項字符串的第一個字符,或者將此腳本保存為“options2.sh”,並使其可執行。

 #!/bin/bash

而 getopts ':abc' 選項; 做
  案例“$OPTION”在 
    一個) 
      echo "使用的選項" 
      ;;

    b)
      echo "使用了選項 b"
      ;;

    C)
      echo "使用了選項 c"
      ;;

    ?) 
      echo "用法:$(basename $0) [-a] [-b] [-c]"
      1號出口
      ;;
  經社理事會
完畢

當我們運行它並產生錯誤時,我們會收到我們自己的錯誤消息,而沒有任何 shell 消息。

 ./options2.sh.sh -d 

僅由腳本報告的無法識別的選項

使用帶有選項參數的 getopts

要告訴getopts一個選項後面會跟一個參數,請在選項字符串中的選項字母后面加上一個冒號“ : ”。

如果我們在選項字符串中的“b”和“c”後面加上冒號, getopt將期望這些選項的參數。 將此腳本複製到您的編輯器中並將其保存為“arguments.sh”,並使其可執行。

請記住,選項字符串中的第一個冒號用於抑制 shell 錯誤消息——它與參數處理無關。

getopt處理帶有參數的選項時,該參數被放置在OPTARG變量中。 如果您想在腳本的其他地方使用此值,則需要將其複製到另一個變量。

 #!/bin/bash

而getopts':ab:c:'選項; 做

  案例“$OPTION”在
    一個)
      echo "使用的選項"
      ;;

    b)
      argB="$OPTARG"
      echo "選項 b 用於:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "選項 c 用於:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 參數] [-c 參數]"
      1號出口
      ;;
  經社理事會

完畢

讓我們運行它,看看它是如何工作的。

 ./arguments.sh -a -b "如何極客" -c reviewgeek
 ./arguments.sh -c reviewgeek -a 

測試可以處理選項參數的腳本

所以現在我們可以處理帶有或不帶參數的選項,而不管它們在命令行中給出的順序。

但是常規參數呢? 我們之前說過,我們知道我們必須在任何選項之後將它們放在命令行上。 讓我們看看如果我們這樣做會發生什麼。

混合選項和參數

我們將更改之前的腳本以包含更多行。 當while循環退出並且所有選項都已處理後,我們將嘗試訪問常規參數。 我們將打印出$1中的值。

將此腳本另存為“arguments2.sh”,並使其可執行。

 #!/bin/bash

而getopts':ab:c:'選項; 做

  案例“$OPTION”在
    一個)
      echo "使用的選項"
      ;;

    b)
      argB="$OPTARG"
      echo "選項 b 用於:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "選項 c 用於:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 參數] [-c 參數]"
      1號出口
      ;;
  經社理事會

完畢

echo "變量一是:$1"

現在我們將嘗試一些選項和參數的組合。

 ./arguments2.sh 戴夫
./arguments2.sh -a 戴夫
./arguments2.sh -a -c how-to-geek 戴夫

無法訪問接受選項參數的腳本中的標準參數

所以現在我們可以看到問題所在。 一旦使用了任何選項,變量$1以後就會被選項標誌及其參數填充。 在最後一個示例中, $4將保存參數值“dave”,但是如果您不知道要使用多少選項和參數,如何在腳本中訪問它?

答案是使用OPTINDshift命令。

shift命令從參數列表中丟棄第一個參數(無論其類型如何)。 其他參數“shuffle down”,因此參數 2 變為參數 1,參數 3 變為參數 2,依此類推。 所以$2變成$1$3變成$2 ,依此類推。

如果您為shift提供一個數字,它將從列表中刪除那麼多參數。

OPTIND在發現和處理選項和參數時對其進行計數。 處理完所有選項和參數後, OPTIND將比選項數大一。 因此,如果我們使用 shift 將(OPTIND-1)參數從參數列表中刪除,我們將在$1之後保留常規參數。

這正是這個腳本所做的。 將此腳本另存為“arguments3.sh”並使其可執行。

 #!/bin/bash

而getopts':ab:c:'選項; 做
  案例“$OPTION”在
    一個)
      echo "使用的選項"
      ;;

    b)
      argB="$OPTARG"
      echo "選項 b 用於:$argB"
      ;;

    C)
      argC="$OPTARG"
      echo "選項 c 用於:$argC"
      ;;

    ?)
      echo "用法:$(basename $0) [-a] [-b 參數] [-c 參數]"
      1號出口
      ;;
  經社理事會
完畢

echo "之前 - 變量一是:$1"
轉移“$(($OPTIND -1))”
echo "之後 - 變量一是:$1"
echo "其餘參數(操作數)"

對於“$@”中的 x
做
  迴聲 $x
完畢

我們將混合使用選項、參數和參數來運行它。

 ./arguments3.sh -a -c how-to-geek “dave dee” dozy beaky mick tich 

在接受選項參數的腳本中正確訪問標準參數

我們可以看到,在調用shift之前, $1持有“-a”,但在 shift 命令之後, $1持有我們的第一個非選項、非參數參數。 我們可以像在沒有選項解析的腳本中一樣輕鬆地遍歷所有參數。

有選擇總是好的

在腳本中處理選項及其參數不需要很複雜。 使用getopts ,您可以創建處理命令行選項、參數和參數的腳本,就像 POSIX 兼容的本機腳本一樣。