Shell Python 脚本命令行参数解析

编写脚本的时候经常需要传递和处理命令行参数,本文介绍一下 Shell 脚本和 Python 脚本中命令行参数的传递和解析。

Shell 脚本参数传递与解析

如下面的一个命令行

sh test.sh --host=localhost -P 5432 -h

我们称 -P 为选项,它需要一个参数,即 5432,-h 也是一个选项,但是不需要参数。–host 称之为长选项,因为选项本身是多字节,它需要一个参数,用等号连接。

在 Bash 中,有三种方式来处理命令行参数,下面分别进行简要介绍:

  1. 手工处理方式
  2. 在手工处理方式中,首先要知道几个变量,以上面的例子为例:

    • $0 : test.sh,即命令本身
    • $1 : –host=localhost
    • $2 : -P
    • $3,$4… : 依次类推
    • $# : 参数的个数,但不包括命令本身,本例中 $#为 4
    • $@ : 参数本身的列表,不包括命令本身,本例中为 –host=localhost -P 5432 -h
    • $* : 和 $@ 相同,但 “$*” 和 “$@”(两个都加引号)不同,第一个将所有的参数组成一个字符串,而第二个是一个参数数组,如下列所示:
    • echo \$@...
      for arg in $@
      do
              echo $arg
      done
      
      echo \$*...
      for arg in $*
      do
              echo $arg
      done
      
      echo \"\$@\"...
      for arg in "$@"
      do
              echo $arg
      done
       
      echo \"\$*\"...
      for arg in "$*"
      do
              echo $arg
      done
      

      运行 sh test.sh –host=localhost -P 5432 -h

      $@...
      --host=localhost
      -P
      5432
      -h
      $*...
      --host=localhost
      -P
      5432
      -h
      "$@"...
      --host=localhost
      -P
      5432
      -h
      "$*"...
      --host=localhost -P 5432 -h
      

      因此,手工处理的方式即对这些参数逐个进行处理,它高度依赖于参数的顺序,一般只用于处理参数数目很少的情况。

  3. getopts
  4. getopts 不支持长选项,是由 Bash 内置的。

    下面来看看参数传递和解析的典型用法:

    • sh test.sh -a -b -c
    • 短选项,各个选项不需要参数

    • sh test.sh -abc
    • 和上面效果相同,只是将所有选项写在一起。

    • sh test.sh -a avalue -b -c
    • 短选项,其中 a 选项需要参数,而 bc 不需要。

    • sh test.sh –along=avalue –blong=bvalue
    • 长选项。

    getopts 不支持长选项,例如对于 sh test.sh -a avalue -b -c 命令,可以编写如下处理参数的脚本:

    #!/bin/bash
    
    while getopts "a:bc" arg # 选项后面的冒号表示需要参数
    do
    	case $arg in
    		a)
    			echo "a = $OPTARG"
    			;;
    		b)
    			echo "b"
    			;;
    		c)
    			echo "c"
    			;;
    		?)
    			echo "Unknow argument:$arg"
    			;;
    		esac
    done
    
    echo "移除已处理的参数"
    shift $((OPTIND - 1))
    echo "处理余下的参数" $@
    or arg in $@
    do
            echo "Processing $arg..."
    done
    

    例如运行 sh test.sh -a avalue -b -c -h haha -g –hehe,上面的脚步输出如下:

    a = avalue
    b
    c
    test.sh: illegal option -- h
    Unknow argument:?
    移除已处理的参数
    处理余下的参数 haha -g --hehe
    Processing haha...
    Processing -g...
    Processing --hehe...
    
  5. getopt
  6. 如果需要使用长选项,那就要用 getopt,getopt 是独立的可执行文件。

    getopts 和 getopt 更多的区别如下:

    • getopts是bash内建命令的, 而getopt是外部命令
    • getopts不支持长选项, 比如: –date
    • 在使用getopt的时候, 每处理完一个位置参数后都需要自己shift来跳到下一个位置, getopts只需要在最后使用shift $(($OPTIND – 1))来跳到parameter的位置。
    • 使用getopt时, 在命令行输入的位置参数是什么, 在getopt中需要保持原样, 比如 -t , 在getopt的case语句中也要使用-t, 而getopts中不要前面的-。
    • getopt往往需要跟set配合使用
    • getopts 使用语法简单,getopt 使用语法较复杂
    • getopts 不会重排所有参数的顺序,getopt 会重排参数顺序
    • getopts 出现的目的是为了代替 getopt 较快捷的执行参数分析工作

    getopt 命令与 getopts 命令不同,它实际上是通过将参数规范化来帮助我们处理的。具体的用法,如下面的脚本:

    #!/bin/bash
    
    #echo $@
    
    #-o或--options选项后面接可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,其中-a选项不接参数,-b选项后必须接参数,-c选项的参数为可选的
    #-l或--long选项后面接可接受的长选项,用逗号分开,冒号的意义同短选项。
    #-n选项后接选项解析错误时提示的脚本名字
    ARGS=`getopt -o ab:c:: --long along,blong:,clong:: -n 'example.sh' -- "$@"`
    if [ $? != 0 ]; then
        echo "Terminating..."
        exit 1
    fi
    
    #echo $ARGS
    #将规范化后的命令行参数分配至位置参数($1,$2,...)
    eval set -- "${ARGS}"
    
    while true
    do
        case "$1" in
            -a|--along) 
                echo "Option a";
                shift
                ;;
            -b|--blong)
                echo "Option b, argument $2";
                shift 2
                ;;
            -c|--clong)
                case "$2" in
                    "")
                        echo "Option c, no argument";
                        shift 2  
                        ;;
                    *)
                        echo "Option c, argument $2";
                        shift 2;
                        ;;
                esac
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Internal error!"
                exit 1
                ;;
        esac
    done
    
    #处理剩余的参数
    for arg in $@
    do
        echo "processing $arg"
    done
    

    需要注意的是,像上面的-c选项,后面是可接可不接参数的,如果需要传递参数给-c选项,则必须使用如下的方式:

    #./getopt.sh -b 123 -a -c456 file1 file2 
    Option b, argument 123
    Option a
    Option c, argument 456
    processing file1
    processing file2
    
    #./getopt.sh --blong 123 -a --clong=456 file1 file2  
    Option b, argument 123
    Option a
    Option c, argument 456
    processing file1
    processing file2
    

Python 脚本参数传递与解析

Python 使用 getopt 模块的 getopt.getopt(args, options, long_options) 函数进行参数解析

args 是要解析的参数列表,不包括脚本本身,通常是 sys.argv[1:];options 是脚本要识别的字母列表,如果选项需要参数值,则需要在后面添加冒号;long_options 是可选参数,它表示长参数,是长参数的字符串列表,不包括前面的两个破折号 ‘–’,如果长参数需要一个参数值,则需要在后面添加一个等号。不支持可选参数。如果只接受长参数,options 应该是一个空字符串。

该函数的返回值包括两个部分,第一部分是(option, value)键值对列表;第二个是选项提取后未处理的剩余参数列表。每个返回的键值对中,第一个是参数名称,如果是短参数,则前面有一个破折号‘-’,如果是长参数则有两个破折号‘–’;第二个就是参数的值,如果没有则为空。返回的键值对列表中参数的顺序和出现顺序相同,因此允许多次出现,也允许长短参数混合。

下面是几个例子:

  1. 只使用 Unix 类型选项:
  2. >>> import getopt
    >>> args = '-a -b -cfoo -d bar a1 a2'.split()
    >>> args
    ['-a', '-b', '-cfoo', '-d', 'bar', 'a1', 'a2']
    >>> optlist, args = getopt.getopt(args, 'abc:d:')
    >>> optlist
    [('-a', ''), ('-b', ''), ('-c', 'foo'), ('-d', 'bar')]
    >>> args
    ['a1', 'a2']
    
  3. 使用长选项:
  4. >>> s = '--condition=foo --testing --output-file abc.def -x a1 a2'
    >>> args = s.split()
    >>> args
    ['--condition=foo', '--testing', '--output-file', 'abc.def', '-x', 'a1', 'a2']
    >>> optlist, args = getopt.getopt(args, 'x', [
    ...     'condition=', 'output-file=', 'testing'])
    >>> optlist
    [('--condition', 'foo'), ('--testing', ''), ('--output-file', 'abc.def'), ('-x', '')]
    >>> args
    ['a1', 'a2']
    
  5. 一个脚本实例:
  6. import getopt, sys
    
    def main():
        try:
            opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
        except getopt.GetoptError as err:
            # print help information and exit:
            print str(err) # will print something like "option -a not recognized"
            usage()
            sys.exit(2)
        output = None
        verbose = False
        for opt, value in opts:
            if opt == "-v":
                print 'verbose = True'
            elif opt in ("-h", "--help"):
                print 'show help message'
            elif opt in ("-o", "--output"):
                print 'output to ' + value
            else:
                assert False, "unhandled option"
    
        # processing left params
        for arg in args:
            if arg == 'start':
                    print 'Processing arg ',arg
            elif arg == 'stop':
                    print 'Processing arg ', arg
            else:
                    print 'Unknow arg ', arg
    
    if __name__ == "__main__":
        main()
    

    运行 python tesh.py -o ‘short out’ -v –output=’long out’ start stop -t,输出如下:

    output to short out
    verbose = True
    output to long out
    Processing arg  start
    Processing arg  stop
    Unknow arg  -t
    

Reference:
bash/shell 解析命令行参数工具:getopts/getopt
使用getopt命令解析shell脚本的命令行选项
Command Line Options: How To Parse In Bash Using “getopt”
getopt — C-style parser for command line options

Tagged on: ,

发表评论

电子邮件地址不会被公开。