如何在 Bash 中解析命令行参数?

问:

说,我有一个用这一行调用的脚本:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

或者这个:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

什么是可接受的解析方式,以便在每种情况下(或两者的某种组合) v 、 v、 vf 和 $d 都将设置为 true 并且 $outFile 将等于 {6 }?

答1:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

Bash 空格分隔(例如,–option 参数)

cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash

POSITIONAL_ARGS=()

while [[ $# -gt 0 ]]; do
  case $1 in
    -e|--extension)
      EXTENSION="$2"
      shift # past argument
      shift # past value
      ;;
    -s|--searchpath)
      SEARCHPATH="$2"
      shift # past argument
      shift # past value
      ;;
    --default)
      DEFAULT=YES
      shift # past argument
      ;;
    -*|--*)
      echo "Unknown option $1"
      exit 1
      ;;
    *)
      POSITIONAL_ARGS+=("$1") # save positional arg
      shift # past argument
      ;;
  esac
done

set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)

if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi
EOF

chmod +x /tmp/demo-space-separated.sh

/tmp/demo-space-separated.sh -e conf -s /etc /etc/hosts

复制粘贴上面的块的输出

FILE EXTENSION  = conf
SEARCH PATH     = /etc
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

用法

demo-space-separated.sh -e conf -s /etc /etc/hosts

Bash Equals-Separated(例如,–option=argument)

cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/bin/bash

for i in "$@"; do
  case $i in
    -e=*|--extension=*)
      EXTENSION="${i#*=}"
      shift # past argument=value
      ;;
    -s=*|--searchpath=*)
      SEARCHPATH="${i#*=}"
      shift # past argument=value
      ;;
    --default)
      DEFAULT=YES
      shift # past argument with no value
      ;;
    -*|--*)
      echo "Unknown option $i"
      exit 1
      ;;
    *)
      ;;
  esac
done

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)

if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi
EOF

chmod +x /tmp/demo-equals-separated.sh

/tmp/demo-equals-separated.sh -e=conf -s=/etc /etc/hosts

复制粘贴上面的块的输出

FILE EXTENSION  = conf
SEARCH PATH     = /etc
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

用法

demo-equals-separated.sh -e=conf -s=/etc /etc/hosts

为了更好地理解 KaTeX parse error: Expected '}', got '#' at position 3: {i#̲*=},请在 this gui…i"或调用 两个 不必要的子流程的echo “$i” | sed ‘s/[^=]*=//’`。

将 bash 与 getopt[s] 一起使用

getopt(1) 限制(较旧、相对较新的 getopt 版本):

无法处理空字符串参数

无法处理带有嵌入空格的参数

较新的 getopt 版本没有这些限制。有关详细信息,请参阅这些 docs。

POSIX getopts

此外,POSIX shell 和其他提供的 getopts 没有这些限制。我包含了一个简单的 getopts 示例。

cat >/tmp/demo-getopts.sh <<'EOF'
#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
  case "$opt" in
    h|\?)
      show_help
      exit 0
      ;;
    v)  verbose=1
      ;;
    f)  output_file=$OPTARG
      ;;
  esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
EOF

chmod +x /tmp/demo-getopts.sh

/tmp/demo-getopts.sh -vf /etc/hosts foo bar

复制粘贴上面的块的输出

verbose=1, output_file='/etc/hosts', Leftovers: foo bar

用法

demo-getopts.sh -vf /etc/hosts foo bar

getopts 的优点是:

它更便携,并且可以在 dash 等其他 shell 中工作。它可以以典型的 Unix 方式自动处理多个单个选项,例如 -vf 文件名。

getopts 的缺点是它只能处理短选项(-h,而不是 --help)而无需额外的代码。

getopts tutorial 解释了所有语法和变量的含义。在 bash 中,还有 help getopts,它可能提供信息。

这是真的吗?根据 Wikipedia,有一个更新的 GNU 增强版本的 getopt,它包括 getopts 的所有功能,然后是一些功能。 Ubuntu 13.04 上的 man getopt 输出 getopt - parse command options (enhanced) 作为名称,所以我认为这个增强版本现在是标准的。

在你的系统上某种方式是一个非常薄弱的前提来建立“标准”的假设。

@Livven,getopt 不是 GNU 实用程序,它是 util-linux 的一部分。

如果您使用 -gt 0,请在 esac 之后删除您的 shift,将所有 shift 增加 1 并添加这种情况:*) break;; 您可以处理非可选参数。例如:pastebin.com/6DJ57HTc

getopts "h?vf:" 应该是不带问号的 getopts "hvf:"。无法识别的参数在 $opt 中存储为 ?。引自 man builtins:“The colon and question mark characters may not be used as option characters.”

答2:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

没有答案展示了增强的 getopt。并且 top-voted answer 具有误导性:它要么忽略 -⁠vfd 样式短选项(由 OP 请求),要么忽略位置参数后的选项(也由 OP 请求);它忽略了解析错误。反而:

使用来自 util-linux 或以前的 GNU glibc.1 的增强型 getopt

它与 GNU glibc 的 C 函数 getopt_long() 一起使用。

此页面上没有其他解决方案可以做到这一切:在 arguments2 中处理空格、引用字符甚至二进制(非增强的 getopt 无法做到这一点)它可以在最后处理选项: script.sh -o outFile file1 file2 -v (getopts 不这样做)允许 =-style 长选项:script.sh --outfile=fileOut --infile fileIn(如果自解析则允许两者都很长)允许组合短选项,例如 -vfd(如果自解析则实际工作) 允许触摸选项参数,例如 -oOutfile 或 -vfdoOutfile

在 arguments2 中处理空格、引用字符甚至二进制(非增强的 getopt 不能这样做)

它可以在最后处理选项: script.sh -o outFile file1 file2 -v (getopts 不这样做)

允许 =-style 长选项: script.sh --outfile=fileOut --infile fileIn (如果自解析,则允许两者都很长)

允许组合短选项,例如 -vfd (如果自我解析,则实际工作)

允许触摸选项参数,例如 -oOutfile 或 -vfdoOutfile

已经太老了3,以至于没有任何 GNU 系统缺少它(例如,任何 Linux 都有它)。

您可以使用以下命令测试它的存在:getopt --test → return value 4。

其他 getopt 或 shell-builtin getopt 的用途有限。

以下调用

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

全部返回

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

使用以下 myscript

#!/bin/bash
# More safety, by turning some bugs into errors.
# Without `errexit` you don’t need ! and can replace
# ${PIPESTATUS[0]} with a simple $?, but I prefer safety.
set -o errexit -o pipefail -o noclobber -o nounset

# -allow a command to fail with !’s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'I’m sorry, `getopt --test` failed in this environment.'
    exit 1
fi

# option --output/-o requires 1 argument
LONGOPTS=debug,force,output:,verbose
OPTIONS=dfo:v

# -regarding ! and PIPESTATUS see above
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 增强的 getopt 可用于大多数“bash 系统”,包括 Cygwin;在 OS X 上尝试 brew install gnu-getopt 或 sudo port install getopt 2 POSIX exec() 约定没有可靠的方法在命令行参数中传递二进制 NULL;这些字节过早地结束了论点 3 1997 年或更早发布的第一个版本(我只追踪到 1997 年)

谢谢你。刚刚从 en.wikipedia.org/wiki/Getopts 的功能表中确认,如果您需要对长选项的支持,并且您不在 Solaris 上,那么 getopt 是可行的方法。

我相信 getopt 的唯一警告是,它不能方便地在包装脚本中使用,其中一个可能只有几个特定于包装脚本的选项,然后将非包装脚本选项传递给包装的可执行文件,完好无损。假设我有一个名为 mygrep 的 grep 包装器,并且我有一个特定于 mygrep 的选项 --foo,那么我不能执行 mygrep --foo -A 2,并让 -A 2 自动传递给 grep;我需要做mygrep --foo -- -A 2。 这里是您解决方案之上的my implementation。

@bobpaul 您关于 util-linux 的陈述是错误的并且具有误导性:该软件包在 Ubuntu/Debian 上被标记为“必需”。因此,它始终被安装。 – 你在谈论哪些发行版(你说它需要故意安装)?

请注意,至少在当前 10.14.3 之前,这在 Mac 上不起作用。发布的 getopt 是 1999 年的 BSD getopt...

@jjj 脚注 1 涵盖 OS X。 – 对于 OS X 开箱即用的解决方案,请查看其他问题和答案。或者说实话:对于真正的编程,不要使用 bash。 ;-)

答3:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

部署.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do
    case $1 in
        -t|--target) target="$2"; shift ;;
        -u|--uglify) uglify=1 ;;
        *) echo "Unknown parameter passed: $1"; exit 1 ;;
    esac
    shift
done

echo "Where to deploy: $target"
echo "Should uglify  : $uglify"

用法:

./deploy.sh -t dev -u

# OR:

./deploy.sh --target dev --uglify

这就是我正在做的事情。如果我想支持以布尔标志 ./script.sh --debug dev --uglify fast --verbose 结束行,则必须 while [[ "$#" > 1 ]]。示例:gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58

哇!简单干净!这就是我使用它的方式:gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58

这比粘贴到每个脚本中要好得多,而不是处理源代码或让人们想知道您的功能实际从哪里开始。

警告:这允许重复的参数,最新的参数为准。例如 ./script.sh -d dev -d prod 将导致 deploy == 'prod'。反正我用过:P :) :+1:

很好的答案,tnx!我把它缩短了一点 - while (( "$#" )); do 而不是 while [[ "$#" -gt 0 ]]; do

答4:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

从 digitalpeer.com 稍作修改:

用法 myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

为了更好地理解 KaTeX parse error: Expected '}', got '#' at position 3: {i#̲*=},请在 this gui…i"或调用 两个 不必要的子流程的echo “$i” | sed ‘s/[^=]*=//’`。

整洁的!虽然这不适用于空格分隔的参数 à la mount -t tempfs ...。可以通过 while [ $# -ge 1 ]; do param=$1; shift; case $param in; -p) prefix=$1; shift;; 等解决此问题

这无法处理 -vfd 样式组合的短选项。

如果您想通用地评估 --option 和 -option 而不是每次都重复 OPTION=$i,请使用 -*=*) 作为匹配模式和 eval ${i##*-}。

答5:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;
    
    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done

这个解决方案:

处理 -n arg 和 --name=arg

最后允许参数

如果有任何拼写错误,则显示正常错误

兼容,不使用 bashisms

可读,不需要在循环中维护状态

抱歉耽搁了。在我的脚本中,handle_argument 函数接收所有非选项参数。您可以将该行替换为您想要的任何内容,可能是 *) die "unrecognized argument: $1" 或将 args 收集到变量 *) args+="$1"; shift 1;; 中。

惊人!我已经测试了几个答案,但这是唯一适用于所有情况的答案,包括许多位置参数(在标志之前和之后)

简洁的代码,但使用 -n 而没有其他 arg 由于 shift 2 上的错误导致无限循环,发出 shift 两次而不是 shift 2。建议编辑。

我进行了编辑(正在等待审核)以添加一些有用的功能,同时保持代码简洁明了。对于更高级的功能,例如单个参数中的多个单字母选项,您应该尝试 getopt 或 getopts。

答6:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

getopt()/getopts() 是一个不错的选择。从 here 复制:

这个小脚本显示了“getopt”的简单用法:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

我们所说的是允许使用 -a、-b、-c 或 -d 中的任何一个,但 -c 后跟一个参数(“c:”表示)。如果我们称其为“g”并尝试一下:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

我们从两个参数开始,“getopt”分解选项并将每个选项放在自己的参数中。它还添加了“–”。

使用 $* 是对 getopt 的错误使用。 (它使用空格来处理参数。)请参阅 my answer 以了解正确用法。

为什么要让它变得更复杂?

@Matt J,如果您使用“$i”而不是$i,脚本的第一部分(对于i)将能够处理其中包含空格的参数。 getopts 似乎无法处理带空格的参数。在 for i 循环中使用 getopt 有什么好处?

答7:

一个优秀的自由职业者,应该有对需求敏感和精准需求捕获的能力,而huntsbot.com提供了这个机会

我发现在脚本中编写可移植解析非常令人沮丧,以至于我编写了 Argbash - 一个 FOSS 代码生成器,可以为您的脚本生成参数解析代码,而且它具有一些不错的功能:

https://argbash.io

感谢您编写 argbash,我刚刚使用它,发现它运行良好。我主要选择 argbash,因为它是支持 OS X 10.11 El Capitan 上的旧 bash 3.x 的代码生成器。唯一的缺点是,与调用模块相比,代码生成器方法意味着您的主脚本中有相当多的代码。

您实际上可以使用 Argbash,它为您生成量身定制的解析库,您可以将其包含在您的脚本中,或者您可以将它放在单独的文件中并直接获取它。我添加了一个 example 来证明这一点,并且我也在文档中更明确地说明了这一点。

很高兴知道。该示例很有趣,但仍然不是很清楚-也许您可以将生成的脚本的名称更改为“parse_lib.sh”或类似名称,并显示主脚本调用它的位置(例如在包装脚本部分中,这是更复杂的用例)。

这些问题已在 argbash 的最新版本中得到解决:文档已得到改进,引入了快速入门 argbash-init 脚本,您甚至可以在 argbash.io/generate 上在线使用 argbash

答8:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

我以较早的答案为起点来整理我旧的临时参数解析。然后我重构了以下模板代码。它处理长参数和短参数,使用 = 或空格分隔的参数,以及组合在一起的多个短参数。最后,它将所有非参数参数重新插入到 $1,$2… 变量中。

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done

此代码无法处理带有如下参数的选项:-c1。并且使用 = 将短选项与其论点分开是不寻常的......

我在这段有用的代码中遇到了两个问题:1)“-c=foo”情况下的“shift”最终吃掉了下一个参数;和 2) 'c' 不应包含在可组合空头期权的“[cfr]”模式中。

答9:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

这允许您同时拥有空格分隔的选项/值,以及相等的定义值。

因此,您可以使用以下命令运行脚本:

./myscript --foo -b -o /fizz/file.txt

也:

./myscript -f --bar -o=/fizz/file.txt

并且两者都应该具有相同的最终结果。

优点:

允许 -arg=value 和 -arg value

适用于您可以在 bash 中使用的任何 arg 名称含义 -a 或 -arg 或 --arg 或 -arg 或其他

含义 -a 或 -arg 或 --arg 或 -arg 或其他

纯粹的狂欢。无需学习/使用 getopt 或 getopts

缺点:

不能组合 args 意味着没有 -abc。你必须做 -a -b -c

意思是没有-abc。你必须做 -a -b -c

我在这里有个问题。为什么使用 shift; OUTPUTFILE="$1" 而不是 OUTPUTFILE="$2"?也许它有一个简单的答案,但我是 bash 的新手

我相信你可以做任何一个,这真的只是个人喜好。在这种情况下,我只想在任何地方都将 $1 作为“活动”参数

答10:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

此示例说明如何使用 getopt 和 eval 以及 HEREDOC 和 shift 来处理带和不带所需值的短参数和长参数。此外,switch/case 语句简洁易懂。

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, dont change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

简短、中肯、易读,并且几乎可以处理所有事情(恕我直言)。

希望对某人有所帮助。

这是最好的答案之一。

答11:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

扩展@bruno-bronosky 的答案,我添加了一个“预处理器”来处理一些常见的格式:

将 --longopt=val 扩展为 --longopt val

将 -xyz 扩展为 -x -y -z

支持 – 表示标志的结束

显示意外选项的错误

紧凑且易于阅读的选项开关

#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename "$0") [options] [--] [file1, ...]"
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage
  exit 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg") ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage; exit 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"

这看起来很棒 - 但想知道 END_OF_OPT=1 是否真的需要在此行:--*) ARGV+=("$arg"); END_OF_OPT=1 ;;。如果留在其中,如果它包含在 --quiet 之后(或任何其他长样式布尔选项),它将无法解析 --username=fred。例如,script.sh --quiet --username=fred 以 Unrecognized argument: --username=fred 失败(尽管 script.sh --quiet --username fred 工作正常)。我在我的脚本中取出了那个 END_OF_OPT=1,现在它可以工作了,但不确定这是否会破坏我不知道的其他情况。

原文链接:https://www.huntsbot.com/qa/Zv8Y/how-do-i-parse-command-line-arguments-in-bash?lang=zh_CN&from=csdn

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值