比如,我有一个脚本,用这行代码调用:

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

或者这个:

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

什么是公认的解析方式,以便在每种情况下(或两者的某种组合)$v、$f和$d都将设置为true,而$outFile将等于/fiz/someOtherFile?


当前回答

这是我使用变量数组对Bruno Bronosky答案的改进解决方案。

它允许您混合参数位置,并为您提供一个参数数组,该数组在没有选项的情况下保持顺序

#!/bin/bash

echo $@

PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
    -n=*|--skip=*)
    SKIP+=("${i#*=}")
    ;;
    -s|--soft)
    SOFT=1
    ;;
    *)
        # unknown option
        PARAMS+=("$i")
    ;;
esac
done
echo "SKIP            = ${SKIP[@]}"
echo "SOFT            = $SOFT"
    echo "Parameters:"
    echo ${PARAMS[@]}

将输出例如:

$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP            = .c .obj
SOFT            = 1
Parameters:
parameter somefile

其他回答

这里有一个getopts,它用最少的代码实现解析,并允许您在一种情况下使用eval和substring定义要提取的内容。

基本上eval“local key='val'”

function myrsync() {

        local backup=("${@}") args=(); while [[ $# -gt 0 ]]; do k="$1";
                case "$k" in
                    ---sourceuser|---sourceurl|---targetuser|---targeturl|---file|---exclude|---include)
                        eval "local ${k:3}='${2}'"; shift; shift    # Past two arguments
                    ;;
                    *)  # Unknown option  
                        args+=("$1"); shift;                        # Past argument only
                    ;;                                              
                esac                                                
        done; set -- "${backup[@]}"                                 # Restore $@


        echo "${sourceurl}"
}

在这里,将变量声明为局部变量,而不是全局变量。

调用为:

myrsync ---sourceurl http://abc.def.g ---sourceuser myuser ... 

${k:3}基本上是一个从键中删除第一个---的子字符串。

部署.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

我最终实现了公认答案的dash(或/bin/sh)版本,基本上不使用数组:

while [[ $# -gt 0 ]]; do
    case "$1" in
    -v|--verbose) verbose=1; shift;;
    -o|--output) if [[ $# -gt 1 && "$2" != -* ]]; then
            file=$2; shift 2
        else
            echo "-o requires file-path" 1>&2; exit 1
        fi ;;
    --)
        while [[ $# -gt 0 ]]; do BACKUP="$BACKUP;$1"; shift; done
        break;;
    *)
        BACKUP="$BACKUP;$1"
        shift
        ;;
    esac
done
# Restore unused arguments.
while [ -n "$BACKUP" ] ; do
    [ ! -z "${BACKUP%%;*}" ] && set -- "$@" "${BACKUP%%;*}"
    [ "$BACKUP" = "${BACKUP/;/}" ] && break
    BACKUP="${BACKUP#*;}"
done

我使用它从末尾迭代key=>值。循环后捕获第一个可选参数。

用法为/script.sh可选的第一个参数-key value-key2 value2

#!/bin/sh

a=$(($#-1))
b=$(($#))
while [ $a -gt 0 ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b-2))
    a=$(($a-2))
done
unset a b key value

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

当然,你可以从左到右做一些改变。

此代码段显示key=>值对和第一个参数(如果存在)。

#!/bin/sh

a=$((1+$#%2))
b=$((1+$a))

[ $(($#%2)) -ne 0 ] && echo "first_arg = $1"

while [ $a -lt $# ]; do
    eval 'key="$'$a'"; value="$'$b'"'
    echo "$key => $value"
    b=$(($b+2))
    a=$(($a+2))
done

unset a b key value

测试了100000个参数,速度很快。

您还可以从左到右迭代key=>value和第一个可选arg,而不使用eval:

#!/bin/sh

a=$(($#%2))
b=0

[ $a -eq 1 ] && echo "first_arg = $1"

for value; do
    if [ $b -gt $a -a $(($b%2)) -ne $a ]; then
        echo "$key => $value"
    fi
    key="$value"
    b=$((1+$b))
done

unset a b key value

这是我的方法-使用正则表达式。

无getopts它处理短参数块-qwerty它处理短参数-q-w-e它处理长选项--qwerty可以将属性传递给短选项或长选项(如果使用的是短选项块,则属性附加到最后一个选项)您可以使用空格或=来提供属性,但属性匹配,直到遇到连字符+空格“分隔符”,所以在--q=qwe ty中,qwe ty是一个属性它处理以上所有内容的混合,因此-o a-op attribute--option=att ribute--option att ribute有效

脚本:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done