Is mixing getopts with positional parameters possible?
我想设计一个shell脚本作为几个脚本的包装。 我想使用
如果
1 2 3 4 5 | myshell.sh -h hostname -s test.sh -d waittime param1 param2 param3 myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh myshell.sh param1 -h hostname -d waittime -s test.sh param2 param3 |
以上所有内容都应该可以称为
1 | test.sh param1 param2 param3 |
是否可以利用
抱歉在旧主题上发表评论,但以为我会为那些正在寻找如何执行此操作的人发布信息...
我想做与OP类似的事情,并且在这里和这里都找到了所需的相关信息。
本质上,如果您想执行以下操作:
1 | script.sh [options] ARG1 ARG2 |
然后像这样获得您的选择:
1 2 3 4 5 6 7 8 | while getopts"h:u:p:d:" flag; do case"$flag" in h) HOSTNAME=$OPTARG;; u) USERNAME=$OPTARG;; p) PASSWORD=$OPTARG;; d) DATABASE=$OPTARG;; esac done |
然后,您可以像下面这样获取位置参数:
1 2 | ARG1=${@:$OPTIND:1} ARG2=${@:$OPTIND+1:1} |
通过上面的链接可以获得更多信息和详细信息。
希望有帮助!!
混合opts和args:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | ARGS="" echo"options :" while [ $# -gt 0 ] do unset OPTIND unset OPTARG while getopts as:c: options do case $options in a) echo"option a no optarg" ;; s) serveur="$OPTARG" echo"option s = $serveur" ;; c) cible="$OPTARG" echo"option c = $cible" ;; esac done shift $((OPTIND-1)) ARGS="${ARGS} $1" shift done echo"ARGS : $ARGS" exit 1 |
结果:
1 2 3 4 5 6 | bash test.sh -a arg1 arg2 -s serveur -c cible arg3 options : option a no optarg option s = serveur option c = cible ARGS : arg1 arg2 arg3 |
最好将param1-3放入其他选项中。
此外,您可以使用现有的库,例如shflags。它非常聪明,易于使用。
最后一种方法是编写自己的函数来解析参数,而无需getopts,只需通过
只需混搭一个快捷方式,即可轻松处理选项和位置参数的混合体(在$ @中仅保留位置参数):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #!/bin/bash while [ ${#} -gt 0 ];do OPTERR=0;OPTIND=1;getopts"p:o:hvu" arg;case"$arg" in p) echo"Path: [$OPTARG]" ;; o) echo"Output: [$OPTARG]" ;; h) echo"Help" ;; v) echo"Version" ;; \?) SET+=("$1") ;; *) echo"Coding error: '-$arg' is not handled by case">&2 ;; esac;shift;["" !="$OPTARG" ] && shift;done [ ${#SET[@]} -gt 0 ] && set"""${SET[@]}" && shift echo -e"========= Leftover (positional) parameters (count=$#) are:" for i in `seq $#`;do echo -e"\t$i> [${!i}]";done |
样本输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@hots:~]$ ./test.sh 'aa bb' -h -v -u -q 'cc dd' -p 'ee ff' 'gg hh' -o ooo Help Version Coding error: '-u' is not handled by case Path: [ee ff] Output: [ooo] ========= Leftover (positional) parameters (count=4) are: 1> [aa bb] 2> [-q] 3> [cc dd] 4> [gg hh] [root@hots:~]$ |
我想了一种方法,可以将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | parse_args() { _parse_args 1"$@" } _parse_args() { local n="$1" shift local options_func="$1" shift local OPTIND "$options_func""$@" shift $(( OPTIND - 1 )) if [ $# -gt 0 ]; then eval test -n \${n$n+x} if [ $? -eq 0 ]; then eval n$n="\$1" fi shift _parse_args $(( n + 1 ))"$options_func""$@" fi } |
然后在OP的情况下,您可以像这样使用它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | main() { local n1='' n2='' n3='' local duration hostname script parse_args parse_main_options"$@" echo"n1 = $n1" echo"n2 = $n2" echo"n3 = $n3" echo"duration = $duration" echo"hostname = $hostname" echo"script = $script" } parse_main_options() { while getopts d:h:s: opt; do case"$opt" in d) duration="$OPTARG" ;; h) hostname="$OPTARG" ;; s) script="$OPTARG" ;; esac done } main"$@" |
运行它会显示输出:
1 2 3 4 5 6 7 | $ myshell.sh param1 param2 -h hostname param3 -d waittime -s test.sh n1 = param1 n2 = param2 n3 = param3 duration = waittime hostname = hostname script = test.sh |
只是概念的证明,但对某人可能有用。
注意:如果使用
您可以直接实现自己的bash参数解析器,而不必使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | #!/bin/bash function parse_command_line() { local named_options; local parsed_positional_arguments; yes_to_all_questions=""; parsed_positional_arguments=0; named_options=( "-y""--yes" "-n""--no" "-h""--help" "-s""--skip" "-v""--version" ); function validateduplicateoptions() { local item; local variabletoset; local namedargument; local argumentvalue; variabletoset="${1}"; namedargument="${2}"; argumentvalue="${3}"; if [[ -z"${namedargument}" ]]; then printf"Error: Missing command line option for named argument '%s', got '%s'...\ ""${variabletoset}""${argumentvalue}"; exit 1; fi; for item in"${named_options[@]}"; do if [["${item}" =="${argumentvalue}" ]]; then printf"Warning: Named argument '%s' got possible invalid option '%s'...\ ""${namedargument}""${argumentvalue}"; exit 1; fi; done; if [[ -n"${!variabletoset}" ]]; then printf"Warning: Overriding the named argument '%s=%s' with '%s'...\ ""${namedargument}""${!variabletoset}""${argumentvalue}"; else printf"Setting '%s' named argument '%s=%s'...\ ""${thing_name}""${namedargument}""${argumentvalue}"; fi; eval"${variabletoset}='${argumentvalue}'"; } # https://stackoverflow.com/questions/2210349/test-whether-string-is-a-valid-integer function validateintegeroption() { local namedargument; local argumentvalue; namedargument="${1}"; argumentvalue="${2}"; if [[ -z"${2}" ]]; then argumentvalue="${1}"; fi; if [[ -n"$(printf"%s""${argumentvalue}" | sed s/[0-9]//g)" ]]; then if [[ -z"${2}" ]]; then printf"Error: The %s positional argument requires a integer, but it got '%s'...\ ""${parsed_positional_arguments}""${argumentvalue}"; else printf"Error: The named argument '%s' requires a integer, but it got '%s'...\ ""${namedargument}""${argumentvalue}"; fi; exit 1; fi; } function validateposisionaloption() { local variabletoset; local argumentvalue; variabletoset="${1}"; argumentvalue="${2}"; if [[ -n"${!variabletoset}" ]]; then printf"Warning: Overriding the %s positional argument '%s=%s' with '%s'...\ ""${parsed_positional_arguments}""${variabletoset}""${!variabletoset}""${argumentvalue}"; else printf"Setting the %s positional argument '%s=%s'...\ ""${parsed_positional_arguments}""${variabletoset}""${argumentvalue}"; fi; eval"${variabletoset}='${argumentvalue}'"; } while [["${#}" -gt 0 ]]; do case ${1} in -y|--yes) yes_to_all_questions="${1}"; printf"Named argument '%s' for yes to all questions was triggered.\ ""${1}"; ;; -n|--no) yes_to_all_questions="${1}"; printf"Named argument '%s' for no to all questions was triggered.\ ""${1}"; ;; -h|--help) printf"Print help here\ "; exit 0; ;; -s|--skip) validateintegeroption"${1}""${2}"; validateduplicateoptions g_installation_model_skip_commands"${1}""${2}"; shift; ;; -v|--version) validateduplicateoptions branch_or_tag"${1}""${2}"; shift; ;; *) parsed_positional_arguments=$((parsed_positional_arguments+1)); case ${parsed_positional_arguments} in 1) validateposisionaloption branch_or_tag"${1}"; ;; 2) validateintegeroption"${1}"; validateposisionaloption g_installation_model_skip_commands"${1}"; ;; *) printf"ERROR: Extra positional command line argument '%s' found.\ ""${1}"; exit 1; ;; esac; ;; esac; shift; done; if [[ -z"${g_installation_model_skip_commands}" ]]; then g_installation_model_skip_commands="0"; fi; } |
您可以将此函数称为:
1 2 3 | #!/bin/bash source ./function_file.sh; parse_command_line"${@}"; |
用法示例:
1 2 3 4 | ./test.sh as 22 -s 3 Setting the 1 positional argument 'branch_or_tag=as'... Setting the 2 positional argument 'skip_commands=22'... Warning: Overriding the named argument '-s=22' with '3'... |
参考文献:
您可以尝试以下技巧:使用optargs进行while循环后,只需使用此代码段
1 2 3 4 5 6 7 8 | #shift away all the options so that only positional agruments #remain in $@ for (( i=0; i<OPTIND-1; i++)); do shift done POSITIONAL="$@" |
但是,这种方法有一个错误:
第一个位置参数之后的所有选项均由getopts合并,并被视为位置参数-如果事件正确,则这些选项(请参见示例输出:-m和-c属于位置参数)
也许还有更多的错误...
看整个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | while getopts :abc opt; do case $opt in a) echo found: -a ;; b) echo found: -b ;; c) echo found: -c ;; \?) echo found bad option: -$OPTARG ;; esac done #OPTIND-1 now points to the first arguments not beginning with - #shift away all the options so that only positional agruments #remain in $@ for (( i=0; i<OPTIND-1; i++)); do shift done POSITIONAL="$@" echo"positional: $POSITIONAL" |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@host ~]# ./abc.sh -abc -de -fgh -bca haha blabla -m -c found: -a found: -b found: -c found bad option: -d found bad option: -e found bad option: -f found bad option: -g found bad option: -h found: -b found: -c found: -a positional: haha blabla -m -c |
Unix选项处理有一些标准,在Shell编程中,
这只是一个简单的例子:
1 | command [ options ] [--] [ words ] |
每个选项必须以破折号
GNU项目引入了Long Options,从两个破折号
后跟一个完整的单词
选项可能会也可能不会出现参数。
任何不以破折号
字符串
剩下的所有参数都保留为位置参数。
开放小组在
埃里克·雷蒙德(Eric Raymond)的Unix编程艺术(Art of Unix Programming)中有一章介绍了传统的unix选项字母及其含义。