Shell-Script

在 getopts 之後解析腳本參數

  • May 6, 2021

我有一個腳本,我在其中使用getopts實現了開關。但是,我無法引用下一個論點。

我的腳本用於在本地開發環境中反向移植我們網站的備份。我添加了一個-p開關來執行一些部署後步驟。這是我的語法:

backport -p /path/to/website_backup.sql.gz

因此,在切換之前,我正在測試是否指定了一個文件,並且它是一個正確的文件。由於文件名路徑是唯一的參數,我可以假設它是必要的,而且它也是第一個參數 ( $1)。

if [[ $# -eq 0 ]] ; then
 echo 'Specifcy the sql file to backport.';
 exit 0;
fi

if [[ ! -f "$1" ]]; then
 echo "$1 is not a valid file.";
 exit 0;
fi

我找到了這個答案,它展示瞭如何使用 getopts 解析開關參數的範例:

while getopts "p" opt; do
 case $opt in
   p) p_post_deploy=true ;; # Handle -a
 esac
done

當然,使用$1as 參數在實現 getopts 後不起作用。沒有開關就好了。但是,當我添加開關時,它當然是第一個參數,我的腳本正在測試它是否是一個文件。

$ backport -p /path/to/website_backup.sql.gz
-p is not a valid file.

因此,隨著開關的引入,我不能依賴出現在命令中特定位置的任何參數。硬編碼$2不起作用,因為如果沒有開關,文件名參數將不是第二個參數。我想要一個解決方案,它允許我在切換後接受參數,並允許我在未來引入新的開關,同時只更新程式碼以自己處理新的開關(並且不必重新洗牌以後可能會移動的參數引入新開關)。

我查看了 unix.stackexchange 問題的答案,該問題指示我如何使用 getopts 解析開關。提到的答案$*之一是表示剩餘參數的變數。

if [[ "$*" -eq 0 ]] ; then
 echo 'Specifcy the sql file to backport.';
 exit 0;
fi

但是,當我嘗試使用它時,我沒有正確表達語法,並且出現解析錯誤。

~/scripts/backport: line 14: [[: /d/Downloads/database.sql.gz: syntax error: operand expected (error token is "/d/Downloads/database.sql.gz")

如何在 getops 之後測試文件名參數?


這是目前版本的腳本:

$ cat backport
#!/bin/bash

set -e

while getopts "p" opt; do
 case $opt in
   p) p_post_deploy=true ;;
 esac
done

shift $(($OPTIND - 1))
# testing what this variable looks like
printf "Remaining arguments are: %s\n" "$*"

if [[ "$*" -eq 0 ]] ; then
 echo 'Specifcy the sql file to backport.';
 exit 0;
fi

if [[ ! -f "$*" ]]; then
 echo "$* is not a valid file.";
 exit 0;
fi

drush @local.dev sql-drop -y ;
zcat $1 | drush @local.dev sqlc ;
drush @local.dev cr;

if [ ! -z "$p_post_deploy" ] ; then
 echo "Running post-deploy..."
 SCRIPT_PATH=$(dirname "$BASH_SOURCE")
 source "$SCRIPT_PATH/post-deploy"
 post_deploy
fi

我只能責怪自己溝通不暢,但 Kusalananda 在評論中給出了我一直在尋找的答案。

使用getopts後解析開關

while getopts "p" opt; do
 case $opt in
   p) p_post_deploy=true ;;
 esac
done

這條線

shift "$((OPTIND - 1))"

將從參數列表中刪除所有開關,以便您可以再次使用位置參數,就像沒有開關一樣。

外殼不會OPTIND自動重置。您必須手動重置它。

您對測試構造的應用

if [[ "$*" -eq 0 ]]

是不正確的。-eq用於整數比較,並且鑑於它$*指的是所有剩餘參數的參數列表,而不是它們的數量,您的測試構造必然會產生語法錯誤,除非該點的剩餘參數列表恰好包含一個元素,還必須碰巧代表一個整數(這不是您想要實現的)。

現在,對於實際任務,我認為只要你允許任意順序(即只要你-<some letter>指定文件名必須放在最後)。如果你想使用getopts,你可能只是想讓文件名也成為一個選項參數,要求它被指定為-f <filename>. 然後你可以使用

while getopts "pf:" opt; do
 case $opt in
   p)
      p_post_deploy=true
   ;;
   f)
      backportfile=$OPTARG
   ;;
 esac
done

getopts如果未指定文件名,則會自動抱怨。

./test.sh: option requires an argument -- f

如果文件存在,您仍然可以稍後進行測試,如

if [[ ! -f "$backportfile" ]]
then
 echo "$backportfile is not a valid file"
 exit 1
fi

引用自:https://unix.stackexchange.com/questions/549129