在 Bash 中解析命令行参数的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14786984/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:19:01  来源:igfitidea点击:

Best way to parse command line args in Bash?

bashcommand-linegetopts

提问by LogicalConfusion

After several days of research, I still can't figure out the best method for parsing cmdline args in a .sh script. According to my references the getopts cmd is the way to go since it "extracts and checks switches without disturbing the positional parameter variables.Unexpected switches, or switches that are missing arguments, are recognized and reportedas errors."

经过几天的研究,我仍然无法找出在 .sh 脚本中解析 cmdline args 的最佳方法。根据我的参考资料,getopts cmd 是要走的路,因为它“在不干扰位置参数变量的情况下提取和检查开关。意外的开关或缺少参数的开关被识别并报告为错误。

Positional params(Ex. 2 - $@, $#, etc) apparently don't work well when spaces are involved but can recognize regular and long parameters(-p and --longparam). I noticed that both methods fail when passing parameters with nested quotes ("this is an Ex. of ""quotes""."). Which one of these three code samples best illustrates the way to deal with cmdline args? The getoptfunction is not recommended by gurus, so I'm trying to avoid it!

位置参数(例如 2 - $@、$# 等)在涉及空格时显然不能很好地工作,但可以识别常规和长参数(-p 和 --longparam)。我注意到在传递带有嵌套引号的参数时,这两种方法都失败了(“这是“引号”的一个例子。“。”)。这三个代码示例中的哪一个最能说明处理 cmdline args 的方法?该getopt的不推荐功能的大师,所以我想,以避免它!

Example 1:

示例 1:

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
    ;;
    -l=*|--lib=*)
    DIR=`echo $i | sed 's/[-a-zA-Z0-9]*=//'`
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
exit 0

Example 2:

示例 2:

#!/bin/bash
echo ‘number of arguments'
echo "$#: $#"
echo ”

echo ‘using $num'
echo "$0: 
#!/bin/bash

while getopts ":a:" opt; do
  case $opt in
    a)
      echo "-a was triggered, Parameter: $OPTARG" >&2
      ;;
    \?)
      echo "Invalid option: -$OPTARG" >&2
      exit 1
      ;;
    :)
      echo "Option -$OPTARG requires an argument." >&2
      exit 1
      ;;
  esac
done

exit 0
" if [ $# -ge 1 ];then echo "$1: "; fi if [ $# -ge 2 ];then echo "$2: "; fi if [ $# -ge 3 ];then echo "$3: "; fi if [ $# -ge 4 ];then echo "$4: "; fi if [ $# -ge 5 ];then echo "$5: "; fi echo ” echo ‘using $@' let i=1 for x in $@; do echo "$i: $x" let i=$i+1 done echo ” echo ‘using $*' let i=1 for x in $*; do echo "$i: $x" let i=$i+1 done echo ” let i=1 echo ‘using shift' while [ $# -gt 0 ] do echo "$i: " let i=$i+1 shift done [/bash] output: bash> commandLineArguments.bash number of arguments $#: 0 using $num
#!/bin/bash

args=$(getopt -l "searchpath:" -o "s:h" -- "$@")

eval set -- "$args"

while [ $# -ge 1 ]; do
        case "" in
                --)
                    # No more options left.
                    shift
                    break
                   ;;
                -s|--searchpath)
                        searchpath=""
                        shift
                        ;;
                -h)
                        echo "Display some help"
                        exit 0
                        ;;
        esac

        shift
done

echo "searchpath: $searchpath"
echo "remaining args: $*"
: ./commandLineArguments.bash using $@ using $* using shift #bash> commandLineArguments.bash "abc def" g h i j*

Example 3:

示例 3:

user@machine:~/bin$ ./getopt_test --searchpath "File with spaces and \"quotes\"."
searchpath: File with spaces and "quotes".
remaining args: other args

回答by Austin Phillips

I find the use of getoptto be the easiest. It provides correct handling of arguments which is tricky otherwise. For example, getoptwill know how to handle arguments to a long option specified on the command line as --arg=optionor --arg option.

我发现使用getopt是最简单的。它提供了对参数的正确处理,否则会很棘手。例如,getopt将知道如何处理在命令行中指定为--arg=optionor的长选项的参数--arg option

What is useful in parsing any input passed to a shell script is the use of the "$@"variables. See the bash man page for how this differs from $@. It ensures that you can process arguments that include spaces.

在解析传递给 shell 脚本的任何输入时有用的是"$@"变量的使用。请参阅 bash 手册页了解这与$@. 它确保您可以处理包含空格的参数。

Here's an example of how I might write s script to parse some simple command line arguments:

这是我如何编写 s 脚本来解析一些简单的命令行参数的示例:

#!/bin/sh
## $PROG 1.0 - Print logs [2017-10-01]
## Compatible with bash and dash/POSIX
## 
## Usage: $PROG [OPTION...] [COMMAND]...
## Options:
##   -i, --log-info         Set log level to info (default)
##   -q, --log-quiet        Set log level to quiet
##   -l, --log MESSAGE      Log a message
## Commands:
##   -h, --help             Displays this help and exists
##   -v, --version          Displays output version and exists
## Examples:
##   $PROG -i myscrip-simple.sh > myscript-full.sh
##   $PROG -r myscrip-full.sh   > myscript-simple.sh
PROG=${0##*/}
LOG=info
die() { echo $@ >&2; exit 2; }

log_info() {
  LOG=info
}
log_quiet() {
  LOG=quiet
}
log() {
  [ $LOG = info ] && echo ""; return 1 ## number of args used
}
help() {
  grep "^##" "
./log.sh --log yep --log-quiet -l nop -i -l yes
" | sed -e "s/^...//" -e "s/$PROG/$PROG/g"; exit 0 } version() { help | head -1 } [ $# = 0 ] && help while [ $# -gt 0 ]; do CMD=$(grep -m 1 -Po "^## *, --\K[^= ]*|^##.* --\K${1#--}(?:[= ])" log.sh | sed -e "s/-/_/g") if [ -z "$CMD" ]; then echo "ERROR: Command '' not supported"; exit 1; fi shift; eval "$CMD" $@ || shift $? 2> /dev/null done

And used like this to show that spaces and quotes are preserved:

并像这样使用以显示保留了空格和引号:

yep
yes

Some basic information about the use of getoptcan be found here

getopt可以在此处找到有关使用的一些基本信息

回答by Tet

If you want to avoid using getoptyou can use this nice quick approach:

如果你想避免使用,getopt你可以使用这个很好的快速方法:

  • Defining help with all options as ##comments (customise as you wish).
  • Define for each option a function with same name.
  • Copy the last five lines of this script to your script (the magic).
  • 将所有选项的帮助定义为##注释(根据需要自定义)。
  • 为每个选项定义一个同名的函数。
  • 将此脚本的最后五行复制到您的脚本中(魔术)。

Example script: log.sh

示例脚本: log.sh

##代码##

Testing

测试

Running this command:

运行此命令:

##代码##

Produces this output:

产生这个输出:

##代码##

By the way:It's compatible with posix!

顺便说一句:它与posix兼容!