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

问题描述 投票:0回答:1

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
解析带有选项参数的多个参数(
:
在每个参数之后)?

bash getopts
1个回答
5
投票

仅针对空头选项,选项和参数之间不需要空格,所以

-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 教程.

如果我理解正确,您的主要目标是检查文件/文件夹是否已传递给

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"
.

© www.soinside.com 2019 - 2024. All rights reserved.