bash - getopts只解析第一个参数,如果需要操作数

时间:2017-11-25 04:44:28

标签: bash getopts

在处理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使用选项参数解析多个参数(每个参数后:)?

1 个答案:

答案 0 :(得分:3)

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

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

您的代码有点混乱,正如@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"