【问题标题】:Shell script argument parsingShell 脚本参数解析
【发布时间】:2011-02-08 05:16:54
【问题描述】:

关于这类事情有很多问题,但假设我们的目标是安装了 getopt 和 getopts 的通用 Linux 系统(我们也不会使用,但它们似乎很受欢迎)

如何解析长参数(--example | --example simple-option)和短参数(-e | -esimple-example | -e simple-example)

【问题讨论】:

    标签: linux parsing shell arguments


    【解决方案1】:

    您想将getopt 与多头和空头选项一起使用。工作代码示例:

    # Parse arguments
    TEMP=$(getopt -n $PROGRAM_NAME -o p:P:cCkhnvVS \
    --long domain-password:,pop3-password:\         
    ,create,cron,kill,help,no-sync-passwords,version,verbose,skip-pop3 \
    -- "$@")                                                            
    
    # Die if they fat finger arguments, this program will be run as root
    [ $? = 0 ] || die "Error parsing arguments. Try $PROGRAM_NAME --help"       
    
    eval set -- "$TEMP"
    while true; do     
            case $1 in 
                    -c|--create)
                            MODE="CREATE"; shift; continue
                    ;;                                    
                    -C|--cron)                            
                            MODE="CRON"; shift; continue  
                    ;;                                    
                    -k|--kill)                            
                            MODE="KILL"; shift; continue  
                    ;;                                    
                    -h|--help)                            
                            usage                         
                            exit 0                        
                    ;;                                    
                    -n|--no-sync-passwords)               
                            SYNC_VHOST=0; shift; continue 
                    ;;                                    
                    -p|--domain-password)                 
                            DOMAIN_PASS="$2"; shift; shift; continue
                    ;;                                              
                    -P|--pop3-password)                             
                            POP3_PASS="$2"; shift; shift; continue  
                    ;;                                              
                    -v|--version)                                   
                            printf "%s, version %s\n" "$PROGRAM_NAME" "$PROGRAM_VERSION"
                            exit 0                                                      
                    ;;                                                                  
                    -v|--verbose)                                                       
                            VERBOSE=1; shift; continue                                  
                    ;;                                                                  
                    -S|--skip-pop3)                                                     
                            SKIP_POP=1; shift; continue                                 
                    ;;                                                                  
                    --)                                                                 
                            # no more arguments to parse                                
                            break                                                       
                    ;;                                                                  
                    *)                                                                  
                            printf "Unknown option %s\n" "$1"                           
                            exit 1                                                      
                    ;;                                                                  
            esac                                                                        
    done     
    

    注意,die 是之前定义的函数(未显示)。

    -n 选项告诉 getopt 将错误报告为我的程序名称,而不是 getopt-o 定义了一个短选项列表(: 在选项之后表示需要的参数),--long 指定长选项列表(对应于短选项)。

    剩下的只是一个简单的开关,适当地调用shift来推进参数指针。请注意,拨打shift; shift; 只是一个顽固的习惯。在当前的现代世界中,shift 2 可能就足够了。

    现代的 getopt 在较新的平台上非常一致,但是您可能会在较旧的(大约 Redhat 9 之前的)系统上遇到一些可移植性问题。有关向后兼容性的信息,请参阅man getopt。但是,您不太可能会遇到对它的需求。

    最后,解析完options后,可以再次调用:

    eval set -- "$@"
    

    这会将参数指针移动到 getopt 完成解析选项后留在命令行上的任何其他内容。然后你可以shift 继续阅读它们。例如,如果一个命令看起来像这样:

    ./foo --option bar file1.txt file2.txt file3.txt
    

    完成后,别忘了创建一个方便的-h / --help 选项来打印您的新奇选项。 :) 如果您使该输出 help2man 友好,那么您就有一个即时手册页可以与您的新工具一起使用。

    编辑

    在大多数发行版中,您可以在 /usr/share/doc/util-linux/examples 中找到更多示例 getopt 代码,默认情况下应该已安装。

    【讨论】:

    • 我确实意识到我在这里没有理智地检查设置模式的多个命令。在这种情况下,最后一个选项优先是可以接受的,并且粘贴只是为了提供有关使用 getopt 的一种方式的信息。
    • 我已经多次被提及使用getopt()。叫我笨蛋,但我就是不明白它是如何工作的。由于我的脚本需要一个等效的 Windows,我 used this。在我看来,这是一种更清洁且不那么神奇的方法。我可以在 linux 上做类似的事情吗?
    • @Christian 你可以用 BASH 做类似的事情,%1 变成 $1(如果 $1 需要一个参数,则分别是 $2)。事实上,很多人就是这样做的,通过shift 不断循环参数并通过开关运行当前参数。 getopt 只是处理所有这些魔法,并为您提供强制参数检查,很多人都喜欢这种便利。
    • 谢谢蒂姆。我明白为什么getop 更好,我只是无法在不到 10 分钟的时间内弄清楚它是如何工作的,与简单的 1:1 翻译相比,这对我的任务来说太多了。另外,让两个脚本都遵循相同的代码模式可以更容易维护。无论如何,感谢您对此的洞察力。