【问题标题】:bash - getopts only parses the first argument if operands are requiredbash - getopts 仅在需要操作数时解析第一个参数
【发布时间】:2018-05-09 00:35:06
【问题描述】:

一旦在处理getops 中的选项时执行了bash 程序,循环就会退出。

作为一个简短的例子,我有以下 bash 脚本:

#!/usr/bin/env bash

while getopts ":a:l:" opt; do
  case ${opt} in
    a)
      ls -a $2
      ;;
    l)
      ls -l $2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument" >&2
      exit 1
      ;;
  esac
done

echo -e "\nTerminated"

如果脚本名为test.sh,当我使用此命令执行脚本时,我会得到以下输出,其中仅处理-a 标志,而忽略-l

$ ./test.sh -al .
.  ..  file1.txt  file2.txt  test.sh

Terminated

但是,如果我删除每个参数后的冒号,表明每个参数不需要操作数,那么脚本会按预期执行。如果while循环改为:

while getopts ":al" opt; do

然后,运行我的脚本会得到以下输出(-a-l 都已处理):

$ ./test.sh -al .
.  ..  file1.txt  file2.txt  test.sh
total 161
-rwxrwxrwx 1 root root   0 Nov 24 22:31 file1.txt
-rwxrwxrwx 1 root root   0 Nov 24 22:32 file2.txt
-rwxrwxrwx 1 root root 318 Nov 24 22:36 test.sh

Terminated

此外,在我的循环末尾添加类似OPTIND=1 的内容只会导致执行第一个参数的脚本无限循环。

我怎样才能让getopts 解析带有选项参数的多个参数(每个参数后有:)?

【问题讨论】:

  • getopts 是正确的。你声明了选项a 来接受一个论点;当你说-al getopt 看到选项a 和参数l
  • 你不应该直接访问$2,你破坏了使用getopts的对象。 -a 的参数在 $OPTARG 中,应遵循 -a-l
  • 考虑不使用“静默模式”(省略:a:l: 中的前导冒号),这样您就可以看到getopts 认为出了什么问题,如果有的话。仅当您确信您的代码正常工作时才使用前导冒号。一般来说,请确保计算机尽快告诉您错误。

标签: bash getopts


【解决方案1】:

仅谈到短选项,选项与其参数之间不需要空格,因此-o something 等于-osomething。尽管将它们分开很常见,但也有一些例外,例如:cut -d: -f1

就像@AlexP 说的,如果你使用while getopts ":a:l:" opt,那么选项-a-l 应该有一个参数。当您将-al 传递给您的脚本并选择-a 以要求参数时,getopts 会查找它并基本上看到:-a l 这就是它忽略-l 选项的原因,因为@987654333 @“吃了”。

您的代码有点乱,正如@cdarke 建议的那样,它没有使用getopts 提供的手段,例如$OPTARG。您可能想查看此getopts tutorial

如果我理解正确,您的主要目标是检查文件/文件夹是否已传递给ls 的脚本。您不会通过使选项需要参数来实现这一点,而是通过检查所有选项之后是否有文件/文件夹。你可以这样做:

#!/usr/bin/env bash

while getopts ":al" opt; do
  case ${opt} in
    a) a=1 ;;
    l) l=1 ;;
    \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
    :) echo "Option -$OPTARG requires an argument" >&2; exit 1 ;;
  esac
done

shift $(( OPTIND - 1 ));

[[ "$#" == 0 ]] && { echo "No input" >&2; exit 2; }

input=("$@")

[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"

echo Done

此解决方案将您由选项触发的选择保存到变量(您可以改用数组),然后根据这些变量进行决定。保存到变量/数组为您提供了更大的灵活性,因为您可以在脚本中的任何位置使用它们。

处理完所有选项后,shift $(( OPTIND - 1 )); 会丢弃所有选项和相关参数,只留下不属于任何选项 = 您的文件/文件夹的参数。如果没有任何文件/文件夹,则使用[[ "$#" == 0 ]] 检测并退出。如果有,请将它们保存到数组 input=("$@") 中,稍后在决定变量时使用此数组:

[[ "$a" == 1 ]] && ls -a "${input[@]}"
[[ "$l" == 1 ]] && ls -l "${input[@]}"

此外,与ls -a $2 不同,使用数组ls -a "${input[@]}" 可以让您传递多个文件/文件夹:./test.sh -la . "$HOME"

【讨论】:

    猜你喜欢
    • 2021-04-29
    • 2016-03-10
    • 2011-02-22
    • 2012-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-02
    相关资源
    最近更新 更多