大量参数(操作数)在命令行参数传递中排在首位

Posted

技术标签:

【中文标题】大量参数(操作数)在命令行参数传递中排在首位【英文标题】:Mass arguments (operands) at first place in command line argument passing 【发布时间】:2015-04-19 10:44:18 【问题描述】:

我使用以下几行(如果不正确,希望这是最佳实践)来处理命令行选项:

#!/usr/bin/bash

read -r -d '' HELP <<EOF
OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -n)
-h    Display this help
EOF

# DECLARE VARIABLES WITH DEFAULT VALUES
color=0
debug=0
verbose=0
download=0
remove=0


OPTIND=1                # Reset in case getopts has been used previously in the shell
invalid_options=();     # Array for invalid options

while getopts ":cdvnrh" opt; do
  echo "Actual opt: $opt"
  case $opt in
    c)
      color=1
      ;;
    d)
      debug=1
      ;;
    v)
      verbose=1
      ;;
    n)
      download=1
      ;;
    r)
      remove=1
      ;;
    h)
      echo "$HELP"
      exit 1
      ;;
   \?)
      invalid_options+=($OPTARG)
      ;;
   *)
      invalid_options+=($OPTARG)
      ;;
  esac
done

# HANDLE INVALID OPTIONS
if [ $#invalid_options[@] -ne 0 ]; then
  echo "Invalid option(s):" >&2
  for i in "$invalid_options[@]"; do
    echo $i >&2
  done
  echo "" >&2
  echo "$HELP" >&2
  exit 1
fi

# SET $1 TO FIRST MASS ARGUMENT, $2 TO SECOND MASS ARGUMENT ETC
shift $((OPTIND - 1))

# HANDLE CORRECT NUMBER OF MASS OPTIONS
if [ $# -ne 2 ]; then
  echo "Correct number of mass arguments are 2"
  echo "" >&2
  echo "$HELP" >&2
  exit 1
fi


# HANDLE MUTUALLY EXCLUSIVE OPTIONS
if [ $download -eq 1 ] && [ $remove -eq 1 ]; then
  echo "Options for download and remove are mutually exclusive" >&2
  echo "$HELP" >&2
  exit 1
fi



echo "color:    $color"
echo "debug:    $debug"
echo "verbose:  $verbose"
echo "download: $download"
echo "remove:   $remove"
echo "\$1:      $1"
echo "\$2:      $2"

如果我以mass arguments(不是开关或开关参数)为最后一个参数的脚本方式调用,一切正常:

$ ./getopts.sh -c -d -v -r a b
Actual opt: c
Actual opt: d
Actual opt: v
Actual opt: r
color:    1
debug:    1
verbose:  1
download: 0
remove:   1
$1:      a
$2:      b

问题是当我想调用脚本时,大量参数是第一个(或者在不使用参数的开关中间的某个地方)

$ ./getopts.sh a b -c -d -v -r
Correct number of mass arguments are 2

OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -d)
-h    Display this help

$ ./getopts.sh -c a b -d -v -r
Actual opt: c
Correct number of mass arguments are 2

OPTIONS:
-c    Enable color output
-d    Enable debug output
-v    Enable verbose output
-n    Only download files (mutually exclusive with -r)
-r    Only remove files (mutually exclusive with -d)
-h    Display this help

我认为根据 (POSIX) 标准这应该没问题,因为以下基本相同的语法在我的系统上按预期工作:

$ cp test1/ test2/ -r
$ cp test1/ -r test2/

我在 Internet 上进行了搜索,但唯一与我的问题相近的是与 C 相关的 this one。

【问题讨论】:

您的观察是正确的:当遇到第一个非选项参数时,getopts 返回退出状态,因此 while 循环结束。我相信getopt 可以以您想要的更灵活的方式工作。 @glennjackman 建议使用 getopts 而不是 getopt。 AFAIK getopt 被认为是过时的。 我不认为 getopt 被认为是过时的。 getopts 更易于使用(IMO:这是我使用的),但它确实会强制您将选项放在所需参数之前,并且您不能使用 --longoptions。 我只是使用普通的case 语句和shift 来获得相同但更通用的效果。 【参考方案1】:

Bash 提供了两种解析参数的方法。

内置命令getopts 是一种更新的、易于使用的解析参数的机制,但它不是很灵活。 getopts 不允许混合选项和批量参数。

外部命令getopt 是一种更古老、更复杂的参数解析机制。它允许长/短选项,并且 gnu 扩展允许混合选项和质量参数。

【讨论】:

【参考方案2】:

getopts 一旦检测到非破折号参数(不包括提供给带参数的 dash 参数的参数),就会自动中断 while 循环。 POSIX 标准是先有虚线参数,然后有文件--+ 也没有这些废话。简单明了。

但是,Linux 不兼容 Unix 或 POSIX。 GNU 实用程序的本质就是比标准的 Unix 实用程序“更好”。更多功能、更多选项和处理方式略有不同。

在 Linux 上,命令行参数可以在许多 GNU 实用程序中文件之后出现。

例如:

$ cp -R foo bar

在我通过 Unix 认证的 Mac OS X 和 Linux 上工作,但是,

$ cp foo bar -R

仅适用于 Linux。

如果您希望 getopts 像许多 Linux 实用程序一样工作,您需要做一些工作。

首先,您必须自己处理您的参数,而不是依赖$OPTIND 来解析它们。您还需要验证您是否有论据。

我想出了这个作为做你想做的事的例子。

#! /bin/bash

while [[ $* ]]
do
    OPTIND=1
    echo $1
    if [[ $1 =~ ^- ]]
    then
        getopts :a:b:cd parameter
        case $parameter in
            a)  echo "a"
                echo "the value is $OPTARG"
                shift
                ;;
            b)  echo "b"
                echo "the value is $OPTARG"
                shift
                ;;
            c)  echo "c"
                ;;
            d)  echo "d"
                ;;
            *) echo "This is an invalid argument: $parameter"
                ;;
        esac
    else
        other_arguments="$other_arguments $1"
    fi
    shift
done
echo "$other_arguments"

只要设置了$*,我现在就会循环。 (也许我应该使用$@?)我必须在循环结束时执行shift。我也每次都将$OPTIND 重置为1,因为我正在转移自己的论点。 $OPTARG 仍然设置,但我必须再做一个 shift 以确保一切正常。

我还必须在if 语句中验证参数是否以破折号开头或未使用正则表达式。

基本测试表明它有效,但我不能说它没有错误,但它确实让您了解如何处理您的程序。

您仍然可以从 getopts 获得大量动力,但确实需要更多的工作。

【讨论】:

以上是关于大量参数(操作数)在命令行参数传递中排在首位的主要内容,如果未能解决你的问题,请参考以下文章

如何将参数(不是命令行参数)传递给批处理脚本中的函数

将命令行参数和文本文件传递给程序

Maven:如何从命令行传递参数运行 .java 文件

解析/传递命令行参数到bash脚本 - “$ @”和“$ *”之间有什么区别?

如何在 C++ 程序中使用命令行参数?

可以传递给 SQL*Plus 的命令行参数的最大长度?