在 getopts 之后解析脚本参数

在 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 解析 switch 参数的示例:

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

当然,$1在实现 getopts 后,使用 as 参数就不起作用了。没有开关就很好了。但是,当我添加开关时,它当然是第一个参数,我的脚本正在测试它是否是一个文件。

$ 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

答案1

我只能怪自己沟通不畅,但 Kusalananda 在评论中给出了我正在寻找的答案。

getopts使用解析开关后

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

这条线

shift "$((OPTIND - 1))"

将从参数列表中删除所有开关,以便您可以再次使用位置参数,就像没有开关一样。

shell 不会OPTIND自动重置。您必须手动重置它。

答案2

您对测试结构的应用

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

是不正确的。-eq是用来整数比较,并且鉴于 指的$*是所有剩余参数的参数列表,而不是它们的数量,您的测试构造必然会产生语法错误,除非此时剩余参数的列表恰好只包含一个元素,这也必须发生代表一个整数(这不是你想要实现的)。

现在,对于实际任务,我认为混合“非选项参数”(即不是-<some letter>只要您允许任意顺序(即只要您不指定文件名必须放在最后),前面带有带有选项参数的“announcing” )。如果您想使用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

相关内容