如何在你的 shell 中使用互斥标志并添加一个可选参数标志(使用 getopts)

Posted

技术标签:

【中文标题】如何在你的 shell 中使用互斥标志并添加一个可选参数标志(使用 getopts)【英文标题】:How to use mutually exclusive flags in your shell and add an optional argument flag ( stuck with getopts) 【发布时间】:2014-03-08 20:49:49 【问题描述】:

我正在使用标准的 getopts 逻辑。但我想知道如何使我提供的选项相互排斥。 例如

shell.sh -a SID 
                              <accepted>
shell.sh -b SID
                              <accepted>
shell.sh -ab SID 
               Message- using ab together is the same as running shell.sh     without   any   options supplying  just SID . Help usage < ya da ya > 
shell.sh 
                Please enter SID at the minimum. Usage < ya da ya >
shell.sh SID
               <accepted>

我正在尝试使用以下内容开发此逻辑

while getopts ":a:b:" opt; do
  case $opt in
  a ) SID="$OPTARG";;
      set var=1
  b ) SID="$OPTARG";;
      set var=2

 \?) echo "Invalid option: -"$OPTARG"" >&2
        exit 1;;
  ) echo "Option -"$OPTARG" requires an argument." >&2
        exit 1;;
  esac
done

If  (( val == 1 ))  then ; # option a is invoked SID supplied 
<stuff>
elif  (( val == 2 )) then ; # option b is invoked SID supplied 

<stuff>

else # SID supplied but neither a or b is invoked 
<stuff>
fi

如何强制执行互斥标志。我确信有更多的杂技演员方法可以做到这一点。我想我在这里遗漏了一些常识 - 并试图弄清楚这一点。 谢谢

$ more opt.ksh
die () 
    echo "ERROR: $*. Aborting." >&2
    return  1


var=
while getopts ":a:b:" opt; do
  case $opt in
      a ) SID="$OPTARG"
          [ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
          [ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
          var=1
          ;;
      b ) SID="$OPTARG"
          [ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
          [ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
          var=2
          ;;
      :)  die "Must supply an argument to $OPTARG"
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"

我正在使用 ksh

$ ps -p $$
  PID TTY          TIME CMD
 1261 pts/45   00:00:00 ksh

我第一次运行它。

$ . opt.ksh -a 123 -b 123  # c0
ERROR: Cannot specify option b after specifying option a. Aborting.
-bash: /home/d1ecom1/gin1: No such file or directory


$ . opt.ksh -ab 123  # c1 should reject "cant use a and b togather. try with a or b"
-nologin: .[25]: shift: 4: bad number
$ . opt.ksh -a -b  # c2  same as c1's message
$ . opt.ksh -a -b 123 # c3 same as c1

$ . opt.ksh -a -b 123  # c5 
$ . opt.ksh -ab 123    # c6
$ . opt.ksh -a 123 -b 123  # c7

以上所有情况 C0:C7 都应该拒绝。注意 C0 和 C7 是一样的。然而,尽管这个 C0 给出了预期的错误,而 C7 不会给出任何错误?奇怪

只有工作的人应该是

. opt.ksh -a 123
. opt.ksh -b 123
. opt.ksh  123

@hvd :TYSM 为您的回复。 我想添加一个附加标志 -p 它将提供“路径覆盖”选项。 所以也许我们必须扩展 getopt 以获取像这样的参数

    die () 
    echo "ERROR: $*. Aborting." >&2
    exit 1


var=
opta=false
optb=false
while getopts ":ab:p" opt; do
  case $opt in
      a ) $optb && die "Cannot specify option a after specifying option b"
          opta=true
          ;;
      b ) $opta && die "Cannot specify option b after specifying option a"
          optb=true
          ;;
      p )  [ -d "$mypath" ] && die "path is invalid"

          ;;

      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
# SID=$1

以上是旧方法。现在我有 2 个输入参数。一个是 SID,另一个是路径。是像上面那样简单还是我需要添加更多检查以防止其他不需要的组合。我想我试图解决的问题是,需要做出更多的规定来允许这个 -p 参数,它是一个可选的覆盖参数。 -p 可以与上面的任何参数共存,但我想一个要求是它应该紧跟在 -p 标志之后 所以这不应该允许 - 因为它不清楚。

shell.sh -b -p 123 /home/yadaya

再次感谢

【问题讨论】:

您为什么将 SID 视为选项参数?如果您只是创建不带任何参数的-a-b 选项(通过删除:),并始终检查是否提供了非选项作为SID,这已经按预期工作了。跨度> 用这些结果更新了帖子。还是卡住了 你确实需要你现在变成 cmets 的最后一部分的第一和第三行 (shift $(($OPTIND - 1)) / [ "$SID" ] || die "You must, at the minimum, supply SID"),以便单独拒绝 -a。我会在短时间内将我的方法扩展为答案。 我实际上想添加一个额外的 -p /path 标志。我更新了我的 Q。只要你有机会。Thx 这可能是您应该提出的新问题,而不是对旧问题的编辑。但是,与您的 -a/-b 不同,在我看来它确实可以作为选项参数(:abp:,而不是 :ab:p)。在您的 p) 案例中,只需设置 mypath=$OPTARG (并根据需要进行验证)。其余检查将保持不变,因为您仍然只有一个非选项参数。然而,虽然-p /path -a 123 似乎显然有效并且会被允许,但-a -p /path 123 也会如此,但-a 123 -p /path 会被拒绝。你可以决定这对你是否有意义。 【参考方案1】:

这使得选项 a 和 b 互斥:

die () 
    echo "ERROR: $*. Aborting." >&2
    exit 1


var=
while getopts ":a:b:" opt; do
  case $opt in
      a ) SID="$OPTARG"
          [ "$var" = 2 ] && die "Cannot specify option a after specifying option b"
          [ "$OPTARG" = b ] && die "Do not specify b as a value for option a"
          var=1
          ;;
      b ) SID="$OPTARG"
          [ "$var" = 1 ] && die "Cannot specify option b after specifying option a"
          [ "$OPTARG" = a ] && die "Do not specify a as a value for option b"
          var=2
          ;;
      :)  die "Must supply an argument to $OPTARG"
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
[ "$SID" ] || SID=$1
[ "$SID" ] || die "You must, at the minimum, supply SID"

请注意,optargs 将 shell.sh -ab SID 解释为选项 a,参数 b 后跟 SID 作为脚本的参数,而不是选项的一部分。为了检测这个问题,添加了上面涉及[ "$OPTARG" = b ] &amp;&amp; die...[ "$OPTARG" = a ] &amp;&amp; die... 的行。

您想接受shell.sh SID,但未指定任何选项。这由 case 语句后的前两行处理。

【讨论】:

您好,对于这种创新方法。它仍然不起作用。我将 die 中的出口更改为 return1 否则它会退出我的登录名。当我使用 return 1 时,结果完全无法预测。我在上面的 Q 中更新了它们【参考方案2】:

在不将 SID 作为选项参数的情况下,您可以这样做,这对我来说更有意义:

die () 
    echo "ERROR: $*. Aborting." >&2
    exit 1


var=
opta=false
optb=false
while getopts ":ab" opt; do
  case $opt in
      a ) $optb && die "Cannot specify option a after specifying option b"
          opta=true
          ;;
      b ) $opta && die "Cannot specify option b after specifying option a"
          optb=true
          ;;
      \?) die "Invalid option: -$OPTARG. Abort"
          ;;
  esac
done
shift $(($OPTIND - 1))
test $# -eq 0 && die "You must supply SID"
test $# -eq 1 || die "Too many command-line arguments"
SID=$1

我所做的更改是:

return 1 变为 exit 1 (与您的问题无关,但 die 听起来不应该继续)。 return 1 只会使 die 函数返回不成功,但对 die 的调用不会检查其结果,无论如何它都会继续。 getopts 字符串是:ab,而不是:a:b:。无论您如何调用您的脚本,您总是需要准确地传递一个 SID,而不管选项如何,因此将它作为选项的一部分包含在我看来是没有意义的。 选项存储在布尔型 optaoptb 变量中,以便于检查 在传递所有选项后,将计算剩余的命令行参数。除非它正好是一个,否则对脚本的调用是无效的。

您在问题中的所有有效呼叫都被接受,所有无效呼叫都被拒绝。不过,对此感兴趣的一点是:您的测试用例 c7 (-a 123 -b 123) 预计会失败,并且确实会失败,但这并不是因为 -a-b 组合在一起。相反,在我的方法中,由于-b出现在非选项参数之后,它本身就是一个非选项参数,拒绝它的原因变成了“命令行参数太多”。

【讨论】:

感谢您的澄清。出口 1 将我踢出 shell :( !!。如果我使用 return 它的行为很不稳定 @user1874594 是的,那是因为您使用. 而不是单独的shell 调用它(对于这样的脚本更常见)。 return 在这里没有任何意义,因为您忽略了 die 函数的退出状态。如果您将其称为ksh opt.ksh [args],它会起作用,而且它有点复杂,超出了我认为您的问题真正要让它在. 表单上正常工作。 TY。所以我只是 +x 它然后直接执行它。让我再试试更新TY TY。所以我只是 +x 它然后直接执行它。让我再次尝试更新 TY。奇迹般有效。 V 有益的学习。 TY【参考方案3】:

对于您必须准确指定选项-s-i-h 之一的情况,并且您还有另外两个选项-v-n,它们是可选的,您可以执行以下操作:

modes=0
while getopts :vs:i:h:n opt; do
  case "$opt" in
    v)
      verbose='non-empty-string-true-flag'
      ;;
    s)
      ((modes++))
      : do something with OPTARG, the s form
      ;;
    i)
      ((modes++))
      : do something with OPTARG, the i form
      ;;
    h)
      ((modes++))
      : do something with OPTARG, the h form
      ;;
    n)
      dryrun='non-empty-string-true-flag'
      ;;
    :)
      printf 'Option -%s requires an argument\n' "$OPTARG" >&2
      usage
      exit 1
      ;;
    *)
      printf 'Invalid option: -%s\n' "$OPTARG" >&2
      usage
      exit 1
      ;;
  esac
done
shift "$((OPTIND-1))"
if [ "$modes" -eq 0 ]; then
  printf "Missing required option\n" >&2
  usage
  exit 1
elif [ "$modes" -gt 1 ]; then
  printf "Error: -h, -i and -s are mutually exclusive and may only be used once\n" >&2
  usage
  exit 1
fi

这是摘自我的一个脚本,为了简化说明,省略了对 sih 参数的处理。

脚本的概要如下:

$0 [ -v ] [ -n ]  -i IPADDRESS | -h HOSTNAME | -s HOSTSHA 

【讨论】:

以上是关于如何在你的 shell 中使用互斥标志并添加一个可选参数标志(使用 getopts)的主要内容,如果未能解决你的问题,请参考以下文章

如何根据 shell 查找结果添加 argparse 标志

Flutter:如何根据 Apple 指南添加 Apple Pay 按钮

使sym链接到可执行文件

如何让shell脚本变成可执行文件

Mac上如何运行shell脚本(变为可执行文件)

如何在你的 wordpress 网站中添加搜索框