web-gelistirme-sc.com

Bash'deki komut satırı argümanlarını nasıl ayrıştırırım?

Diyelim ki, bu satırla çağrılan bir betiğim var:

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

ya da bu:

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

Bunu ayrıştırmanın kabul edilen yolu nedir? Öyle ki, her durumda (ya da ikisinin bir kombinasyonunun) $v, $f ve $d 'nin tümü true' a ayarlanacak ve $outFile/fizz/someOtherFile 'ya eşit olacaktır.

1521

Yöntem # 1: getopt olmadan bash kullanımı [s]

Anahtar-değer çifti argümanlarını iletmenin iki yaygın yolu şunlardır:

Bash Uzay Ayrılmış (örneğin, --option argument) (getopt [s] olmadan)

Kullanımı ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts

#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo FILE EXTENSION  = "${EXTENSION}"
echo SEARCH PATH     = "${SEARCHPATH}"
echo LIBRARY PATH    = "${LIBPATH}"
echo DEFAULT         = "${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi

Bash Eşittir-Ayrılmış (örneğin, --option=argument) (getopt [s] olmadan)

Kullanımı ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

#!/bin/bash

for i in "[email protected]"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi

Daha iyi anlamak için ${i#*=}bu kılavuz içindeki "Substring Removal" ifadesini arayın. İşlevsel olarak gereksiz bir alt işlemi çağıran `sed 's/[^=]*=//' <<< "$i"`'e veya iki gereksiz alt işlemi çağıran `echo "$i" | sed 's/[^=]*=//'` işlevine eşdeğerdir. 

Yöntem # 2: getopt [s] ile bash kullanma

from: http://mywiki.wooledge.org/BashFAQ/035#getopts

getopt (1) sınırlamaları (daha eski, nispeten yeni getopt sürümleri): 

  • boş dize olan bağımsız değişkenleri işleyemez
  • gömülü boşluk içeren değişkenlerle baş edemez

Daha yeni getopt sürümlerinde bu sınırlamalar yoktur.

Ek olarak, POSIX Kabuğu (ve diğerleri), bu sınırlamaları olmayan getopts öğesini sunar. İşte basit bir getopts örneği:

#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the Shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: [email protected]"

# End of file

getopts'nin avantajları:

  1. Daha taşınabilir ve dash gibi diğer mermilerde çalışacak. 
  2. -vf filename gibi birden fazla tek seçeneği tipik Unix biçiminde otomatik olarak idare edebilir.

getopts'nin dezavantajı, ek kod olmadan yalnızca kısa seçenekleri (-h, --help değil) kaldırabilmesidir.

Tüm sözdizimi ve değişkenlerin ne anlama geldiğini açıklayan bir getopts tutorial vardır. Kısaca, bilgi verici olabilecek help getopts de vardır.

2173
Bruno Bronosky

Yanıt yok gelişmiş getopt ifadesinden bahseder. Ve en çok oy alan cevap yanıltıcıdır:-⁠vfd stili kısa seçenekleri (OP tarafından talep edilir), konumsal argümanlardan sonraki seçenekleri (OP tarafından da talep edilir) ve ayrıştırma hatalarını yoksayar. Yerine:

  • Gelişmiş getopt util util-linux veya daha önce kullanın GNU glibc.1
  • getopt_long() ile çalışır: GNU glibc C fonksiyonu.
  • Tümü kullanışlı ayırt edici özelliği var (diğerleri yok):
    • boşlukları ele alır, karakterleri alıntılar ve hatta argümanlarda ikili bile kullanırlar2
    • sonunda seçenekleri işleyebilir: script.sh -o outFile file1 file2 -v
    • =-stili uzun seçeneklere izin verir: script.sh --outfile=fileOut --infile fileIn
  • Çok eski3 o no GNU sistem bunu kaçırıyor (örneğin herhangi bir Linux'ta var).
  • Varlığını test edebilirsiniz: getopt --test → return değer 4.
  • Diğer getopt veya Shell-builtin getopts, sınırlı kullanım içindir.

Aşağıdaki aramalar

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

tüm dönüş

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

aşağıdaki myscript ile

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'I’m sorry, `getopt --test` failed in this environment.'
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -use ! and PIPESTATUS to get exit code with errexit set
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "[email protected]"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "[email protected]")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 geliştirilmiş getopt, Cygwin dahil çoğu bash sisteminde mevcuttur; OS X üzerinde demlemek yükleme gnu-getopt veya Sudo port install getopt
2 POSIX exec() kurallarının komut satırı argümanlarında ikili NULL'yi geçmenin güvenilir bir yolu yoktur; bu baytlar argümanı erken bitirir
3 1997'de veya daha önce yayınlanan ilk sürüm (yalnızca 1997'ye kadar izledim)

419
Robert Siemer

from: digitalpeer.com küçük değişikliklerle

Kullanımı myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "[email protected]"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

Daha iyi anlamak için ${i#*=}bu kılavuz içindeki "Substring Removal" ifadesini arayın. İşlevsel olarak gereksiz bir alt işlemi çağıran `sed 's/[^=]*=//' <<< "$i"`'e veya iki gereksiz alt işlemi çağıran `echo "$i" | sed 's/[^=]*=//'` işlevine eşdeğerdir.

114
guneysus

getopt()/getopts(), iyi bir seçenektir. buradan : çalındı

Bu ge-script'de "getopt" un basit kullanımı gösterilmektedir:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Söylediğimiz şey, herhangi birinin -a, -b, -c veya -d'ye izin verilir, ancak bu -c argümanını takip eder ("c:" diyor).

Eğer buna "g" diyoruz ve deneyelim:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

İki argümanla başlıyoruz ve. "getopt" seçenekleri ayırır ve her birini kendi bağımsız değişkenine koyar. Aynı zamanda. katma "--".

102
Matt J

Görmezden gelmek için başka bir örnek ekleme riski altında, işte benim planım.

  • kolları -n arg ve --name=arg
  • sonunda tartışmalara izin verir
  • bir şey yanlış yazılmışsa aklı başında hataları gösterir
  • uyumlu, temel bilgiler kullanmıyor
  • okunabilir, bir döngüde durumun korunmasını gerektirmez

Umarım biri için yararlıdır.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done
84
bronson

Daha özlü bir şekilde

script.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do case $1 in
  -d|--deploy) deploy="$2"; shift;;
  -u|--uglify) uglify=1;;
  *) echo "Unknown parameter passed: $1"; exit 1;;
esac; shift; done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

Kullanım:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify
78
Inanc Gumus

Bu soruya yaklaşık 4 yıl geciktim ama geri vermek istiyorum. Daha önceki cevapları eski geçici param ayrıştırmamı düzeltmek için bir başlangıç ​​noktası olarak kullandım. Daha sonra aşağıdaki şablon kodunu yeniden düzenlendi. Hem uzun hem de kısa paragrafları, = veya boşlukla ayrılmış argümanların yanı sıra birlikte gruplanmış çoklu kısa paramları kullanır. Sonunda, param olmayan bağımsız değişkenleri tekrar 1 $, 2 $ değişkenlerine yeniden ekler. Umarım faydalıdır.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 [email protected] ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable Shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the Shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done
39
Shane Day

Cevabım büyük ölçüde Bruno Bronosky'nin cevabına dayanıyor , ama iki saf bash uygulamasının oldukça sık kullandığım birine çarptım.

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

Bu, hem boşlukla ayrılmış seçeneklere/değerlere hem de eşit tanımlanmış değerlere sahip olmanızı sağlar.

Böylece betiğinizi kullanarak çalıştırabilirsiniz:

./myscript --foo -b -o /fizz/file.txt

hem de:

./myscript -f --bar -o=/fizz/file.txt

ve her ikisi de aynı sonuca sahip olmalıdır.

Artıları:

  • -Arg = value ve -arg değerlerinin ikisine de izin verir

  • Bash'da kullanabileceğiniz herhangi bir arg ismiyle çalışır

    • Anlamı -a veya -arg veya - arg veya -a-r-g veya her neyse
  • Saf bash. Getopt veya getopts öğrenmeye/kullanmaya gerek yok

EKSİLERİ:

  • Args birleştirilemiyor

    • Anlamı hayır-abc. Yapmalısın -a -b -c

Bunlar kafamın üstünden düşünebildiğim tek artı/eksiler.

25
Ponyboy47

Komut dosyalarına taşınabilir ayrıştırma yazma meselesini o kadar sinir bozucu buldum ki, Argbash - bir FOSS kod üreteci yazdım.

https://argbash.io

24
bubla

@Guneysus tarafından verilen mükemmel yanıtı genişleterek, kullanıcıların tercih ettikleri sözdizimini kullanmalarına izin veren bir Tweak, örn.

command -x=myfilename.ext --another_switch 

vs

command -x myfilename.ext --another_switch

Yani, eşittir boşluklarla değiştirilebilir. 

Bu "bulanık yorum" sizin hoşunuza gitmeyebilir, ancak diğer yardımcı programlarla değiştirilebilen senaryolar (ffmpeg ile çalışması gereken madende olduğu gibi) yapıyorsanız, esneklik yararlı olacaktır.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "[email protected]"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done
13
unsynchronized

Bunun kullanmak için yeterince basit olduğunu düşünüyorum:

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Başvuru örneği:

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile
13
Alek

Size komut satırından paraşütleri ayıracak olan Function parse_params işlevini veriyorum.

  1. Bu, saf bir Bash çözümüdür, ek bir yardımcı program yoktur.
  2. Küresel kapsamı kirletmez. 
  3. Zahmetsizce, daha fazla mantık oluşturabileceğiniz değişkenleri kullanmak için basitçe döndürür.
  4. Params öncesi çizgi sayısı önemli değil (--all, -all, all=all'a eşittir)

Aşağıdaki script bir kopyala yapıştır çalışma gösterimidir. show_use'nin nasıl kullanılacağını anlamak için parse_params işlevine bakın.

Sınırlamalar: 

  1. Alanla sınırlandırılmış paraşütleri desteklemez (-d 1)
  2. Param isimleri kısa çizgiler kaybeder, bu yüzden --any-param ve -anyparam eşdeğerdir
  3. eval $(parse_params "[email protected]") bash function içinde kullanılmalıdır (global kapsamda çalışmayacaktır)

#!/bin/bash

# Universal Bash parameter parsing
# Parses equal sign separated params into local variables (--name=bob creates variable $name=="bob")
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Parses un-named params into ${ARGV[*]} array
# Additionally puts all named params raw into ${ARGN[*]} array
# Additionally puts all standalone "option" params raw into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4 (Jun-26-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion
        _escaped=${1/\*/\'\"*\"\'}
        # If equals delimited named parameter
        if [[ "$1" =~ ^..*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key=\"$_val\";"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        Elif [[ "$1" =~ ^\-. ]]; then
            # remove dashes
            local _key=${1//\-}
            # skip when key is empty
            if [[ "$_key" == "" ]]; then
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "[email protected]")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2
8

getopts # 1 yüklü olduğunda ve # 2 aynı platformda çalıştırmayı düşünüyorsanız harika çalışır. OSX ve Linux (örneğin) bu açıdan farklı davranırlar.

İşte eşit, eşit olmayan ve boolean bayrakları destekleyen (getopts olmayan) bir çözüm. Örneğin, komut dosyanızı şu şekilde çalıştırabilirsiniz:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("[email protected]")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        Elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        Elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done
8
vangorra

EasyOptions / ayrıştırma gerektirmez:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi
7
Renato Silva

Bu aynı zamanda yığında daha yüksek bir yerde aynı anda çalıştırmak getopts kırılmasını önlemek için bir işlevi yapmak:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local Host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         Host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}
6
akostadinov

Aşağıdakilere izin veren, seçenek ayrıştırma sürümümü sunmak istiyorum:

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Ayrıca bunun için izin verir (istenmeyen olabilir):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Bir seçeneğin üzerinde = kullanılıp kullanılmayacağına = kullanmadan önce karar vermelisin. Bu, kodu temiz tutmaktır (ish).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done
4
galmok

getopt(1) işlevinin AT&T tarafından kısa bir yaşam hatası olduğunu unutmayın.

getopt 1984'te kuruldu, ancak 1986'da gömüldü çünkü gerçekten kullanılamıyordu.

getopt öğesinin çok eski olduğuna dair bir kanıt, getopt(1) man sayfasının hala "$*" yerine "[email protected]"'dan bahsettiğidir, 1986'da Bourne Kabuğuna, getopts(1) Shell yerleşiği ile birlikte boşluklarla başa çıkmak için eklenir.

BTW: Shell komut dosyalarında uzun seçeneklerin ayrıştırılmasıyla ilgileniyorsanız, libc (Solaris) ve ksh93 adreslerinden getopt(3) uygulamasının hem kısa seçenekler için takma ad olarak uzun seçenekleri destekleyen tek bir uzun seçenek uygulaması eklediğini bilmek ilgi çekici olabilir. Bu, ksh93 ve Bourne Shell öğelerinin getopts yoluyla uzun seçenekler için tek bir arabirim uygulamasına neden olur.

Bourne Shell man sayfasından alınan uzun seçeneklere bir örnek:

getopts "f:(file)(input-file)o:(output-file)" OPTX "[email protected]"

bourne Kabuğu ve ksh93'te seçenek takma adlarının ne kadar süreyle kullanılabileceğini gösterir.

Bourne Shell'in yeni sayfasına bakınız:

http://schillix.sourceforge.net/man/man1/bosh.1.html

ve OpenSolaris'ten getopt (3) için man sayfası:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

ve son olarak, getopt (1) kılavuz sayfası, tarihi geçmiş $ * değerini doğrulamak için:

http://schillix.sourceforge.net/man/man1/getopt.1.html

4
schily

Aşağıdaki gibi test_args.sh adlı bir Shell betiği yarattığımızı varsayalım

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

Aşağıdaki komutu çalıştırdıktan sonra:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

Çıktı şöyle olurdu:

year=2017 month=12 day=22 flag=true
3
John

Konumsal ve bayrak temelli argümanları karıştırmak

--param = arg (sınırlandırılmış eşittir)

Konumsal argümanlar arasında bayrakları serbestçe karıştırmak:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

oldukça özlü bir yaklaşımla gerçekleştirilebilir:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

--param argümanı (boşlukla ayrılmış)

--flag=value ve --flag value stillerini karıştırmamak genellikle daha kolaydır.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Bu okumak biraz zor, ama yine de geçerli

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Kaynak

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2
2
Mark Fox

Nice bir bash aracı yazmak için bir bash yardımcısı yazdım

proje evi: https://gitlab.mbedsys.org/mbedsys/bashopts

örnek:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "[email protected]"

# Process argument
bashopts_process_args

yardım edecek:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

keyfini çıkarın :)

2
Emeric Verschuur

İşte benim yaklaşım - regexp kullanarak.

  • getopts yok
  • kısa parametrelerin bloğunu işler -qwerty
  • kısa parametreleri kullanır -q -w -e
  • uzun seçenekleri kullanır --qwerty
  • özniteliği kısa veya uzun seçeneğe geçirebilirsiniz (kısa seçenekler bloğu kullanıyorsanız, öznitelik son seçeneğe eklenir)
  • nitelikleri sağlamak için boşluk veya = kullanabilirsiniz, ancak tire + boşluk "sınırlayıcı" ile karşılaşıncaya kadar eşleşen özellikleri kullanabilirsiniz, bu nedenle --q=qwe ty içinde _ qwe ty bir özelliktir
  • her şeyin üstündeki karışımı kullanır, böylece -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute geçerlidir

senaryo:

#!/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
}
[email protected]

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
2
a_z

İşlenmeyen argümanları koruyan çözüm. Demolar Dahil.

İşte benim çözümüm. ÇOK esnektir ve diğerlerinden farklı olarak dış paketler gerektirmemeli ve artık argümanları temiz bir şekilde ele almalıdır.

Kullanım: ./myscript -flag flagvariable -otherflag flagvar2

Tek yapmanız gereken validflags satırını düzenlemek. Kısa çizgi hazırlar ve tüm argümanları arar. Daha sonra bir sonraki argümanı bayrak ismi olarak tanımlar.

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Ana kod (kısa versiyon, daha aşağı örneklerle ayrıntılı, ayrıca hata içeren bir sürüm):

#!/usr/bin/env bash
#Shebang.io
validflags="rate time number"
count=1
for arg in [email protected]
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

Dahili yankı göstergelerine sahip ayrıntılı sürüm:

#!/usr/bin/env bash
#Shebang.io
rate=30
time=30
number=30
echo "all args
[email protected]"
validflags="rate time number"
count=1
for arg in [email protected]
do
    match=0
    argval=$1
#   argval=$(echo [email protected] | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
[email protected]"
shift $#
echo "post final clear args:
[email protected]"
set -- $leftovers
echo "all post set args:
[email protected]"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Sonuncusu, geçersiz bir argüman iletilirse, bu bir hata verir.

#!/usr/bin/env bash
#Shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in [email protected]
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Artıları: Ne yapar, çok iyi idare eder. Buradaki diğer çözümlerin çoğunun kullanmadığı, kullanılmayan argümanları korur. Ayrıca, kodda elle tanımlanmadan değişkenlerin çağrılmasını sağlar. Karşılık gelen bir argüman verilmezse, değişkenlerin önceden hazırlanmasına da izin verir. (Ayrıntılı örneğe bakın).

Eksileri: Tek bir karmaşık argüman dizgesini ayrıştıramazsınız, ör. -xcvf, tek bir argüman olarak işlem görür. Yine de bu işlevselliği ekleyen benimkine kolaylıkla kolayca ek kod yazabilirsiniz. 

2
Noah

Getopt [s], POSIX, eski Unix stili olmayan başka bir çözüm

Çözüm Bruno Bronosky'nin yayınladığı gibi bu, getopt(s) kullanmadan yapılandır.

Çözümümün temel ayırt edici özelliği, aynı tar -xzf foo.tar.gz gibi seçeneklerin birlikte tar -x -z -f foo.tar.gz 'e eşit olmasıyla bir araya getirilmiş seçeneklere sahip olmasını sağlamasıdır. Ayrıca tar, ps vb. Gibi baştaki tire kısa seçeneklerden oluşan bir seçenek için isteğe bağlıdır (ancak bu kolayca değiştirilebilir). Uzun seçenekler de desteklenir (ancak bir blok bir ile başladığında iki öncü tire gerekir).

Örnek seçenekli kod

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from [email protected][se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  Elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Örnek kullanım için lütfen aşağıdaki örneklere bakınız.

Seçeneklerin argümanlarla konumu

Neye değerse, argümanlar olan seçenekler son değil (sadece uzun seçeneklerin olması gerekiyor). Öyleyse, örneğin tar dosyasında (en azından bazı uygulamalarda) f seçeneklerinin en son olması gerekir çünkü dosya adının izlemesi gerekir (tar xzf bar.tar.gz çalışır ancak tar xfz bar.tar.gz yok) bu durum böyle değil (sonraki örneklere bakın).

Argümanlar ile çoklu seçenekler

Diğer bir bonus olarak, seçenek parametreleri, seçeneklerin yer aldığı seçeneklere göre gerekli seçeneklere göre tüketilir. Sadece betiğimin çıktısına, abc X Y Z (veya -abc X Y Z) komut satırına bakın:

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Uzun seçenekler de birleştirilmiş

Ayrıca, seçenek bloğunda, blokta en son meydana geldiklerinden dolayı uzun seçeneklere sahip olabilirsiniz. Bu nedenle, aşağıdaki komut satırlarının hepsi eşdeğerdir (seçeneklerin ve bağımsız değişkenlerin işlenme sırası dahil):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Bunların hepsi şunlara yol açar:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Bu çözümde yok

İsteğe bağlı argümanlar

İsteğe bağlı argümanlara sahip seçenekler, biraz çalışarak mümkün olmalıdır; tire olmadan bir blok olup olmadığını dört gözle; kullanıcının daha sonra isteğe bağlı bir parametreye sahip bir parametreye sahip bir bloğu izleyen her bloğun önüne bir tire koyması gerekir. Belki de bu, kullanıcıyla iletişim kurmak için çok karmaşıktır; bu durumda, sadece bu durumda tamamen lider bir tire gerekir.

Birden fazla olası parametreyle işler daha da karmaşıklaşıyor. Argümanın bunun için olup olmayacağını belirleyerek (örneğin, bir seçenekle sadece isteğe bağlı bir argüman olarak bir sayı alır) belirleyerek akıllıca çalışmayı sağlayan seçeneklerin yapılmamasını tavsiye ederim, çünkü bu gelecekte bozulabilir.

Kişisel olarak isteğe bağlı argümanlar yerine ek seçenekleri tercih ederim.

Eşit işareti ile sunulan seçenek argümanları

İsteğe bağlı argümanlarda olduğu gibi ben de bunun hayranı değilim (BTW, farklı parametre stillerinin artılarını/eksilerini tartışmak için bir konu var mı?) Ama eğer bunu isterseniz http'de olduğu gibi kendiniz de uygulayabilirsiniz. //mywiki.wooledge.org/BashFAQ/035#Manual_loop ile bir --long-with-arg=?* case deyimi ve sonra eşit işareti sıyırma (bu, BTW parametresi birleştirmenin bir çaba ile mümkün olduğunu söyleyen sitedir, ancak "bırak] Okuyucu için bir alıştırma "onları Word'lerine götürmeme neden oldu ama sıfırdan başladım).

Diğer notlar

POSIX uyumlu, uğraşmak zorunda olduğum eski Busybox kurulumlarında bile çalışıyor (örneğin cut, head ve getopts eksik).

1
phk

Bu aynı zamanda bilmek yararlı olabilir, bir değer belirleyebilirsiniz ve eğer birisi girdi sağlıyorsa, bu değeri içeren varsayılan ayarları geçersiz kılar. 

myscript.sh -f ./serverlist.txt veya sadece ./myscript.sh (ve varsayılanları alır)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"
1
Mike Q

Değişken dizileri kullanarak Bruno Bronosky'nin cevabındaki gelişmiş çözümüm.

parametrelerin konumunu karıştırmanıza ve seçenekleri seçenek olmadan sırayı koruyan bir parametre dizisi vermenize olanak

#!/bin/bash

echo [email protected]

PARAMS=()
SOFT=0
SKIP=()
for i in "[email protected]"
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[@]}

Örneğin çıkacak:

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

@ Bruno-bronosky'nin yanıtını genişleterek, bazı yaygın biçimlendirmeleri işlemek için bir "önişlemci" ekledim:

  • --longopt=val öğesini --longopt val içine genişletir
  • -xyz öğesini -x -y -z içine genişletir
  • Bayrakların sonunu belirtmek için -- desteği
  • Beklenmeyen seçenekler için hata gösterir
  • Kompakt ve kolay okunur seçenekler anahtarı
#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename $0) [options] [--] [file1, ...]"

  # Optionally exit with a status code
  if [ -n "$1" ]; then
    exit "$1"
  fi
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg"); END_OF_OPT=1 ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"
1
jchook

Bu örnek, aşağıdaki gerekli bir değere sahip olan ve olmayan kısa ve uzun parametreleri işlemek için getopt ve eval ve HEREDOC ve shift öğelerinin nasıl kullanıldığını gösterir. Ayrıca switch/case deyimi özlü ve takip etmesi kolaydır.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, don't change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "[email protected]")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Yukarıdaki betiğin en önemli satırları:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "[email protected]")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Kısa, noktaya, okunabilir ve hemen hemen her şeyi (IMHO) ele alır.

Birisi yardımcı olur umarım.

1
phyatt

bash-module konumundan "arguments" modülünü kullanın

Örnek:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "[email protected]" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"

Projemi göndermek istiyorum: https://github.com/flyingangel/argparser

source argparser.sh
parse_args "[email protected]"

Bu kadar basit. Ortam, argümanlarla aynı isimde değişkenlerle doldurulur.

1
Thanh Trung

Bu sorunun en iyi cevabı, denediğimde biraz rahatsız edici gözüküyordu - işte daha sağlam bulduğum çözümüm:

boolean_arg=""
arg_with_value=""

while [[ $# -gt 0 ]]
do
key="$1"
case $key in
    -b|--boolean-arg)
    boolean_arg=true
    shift
    ;;
    -a|--arg-with-value)
    arg_with_value="$2"
    shift
    shift
    ;;
    -*)
    echo "Unknown option: $1"
    exit 1
    ;;
    *)
    arg_num=$(( $arg_num + 1 ))
    case $arg_num in
        1)
        first_normal_arg="$1"
        shift
        ;;
        2)
        second_normal_arg="$1"
        shift
        ;;
        *)
        bad_args=TRUE
    esac
    ;;
esac
done

# Handy to have this here when adding arguments to
# see if they're working. Just edit the '0' to be '1'.
if [[ 0 == 1 ]]; then
    echo "first_normal_arg: $first_normal_arg"
    echo "second_normal_arg: $second_normal_arg"
    echo "boolean_arg: $boolean_arg"
    echo "arg_with_value: $arg_with_value"
    exit 0
fi

if [[ $bad_args == TRUE || $arg_num < 2 ]]; then
    echo "Usage: $(basename "$0") <first-normal-arg> <second-normal-arg> [--boolean-arg] [--arg-with-value VALUE]"
    exit 1
fi
1
Daniel Bigham

Değiştirilmesi basit ve kolay, parametreler herhangi bir sırayla olabilir. bu, herhangi bir biçimde parametreleri almak için değiştirilebilir (-a, --a, a, etc).

for arg in "[email protected]"
do
   key=$(echo $arg | cut -f1 -d=)`
   value=$(echo $arg | cut -f2 -d=)`
   case "$key" in
        name|-name)      read_name=$value;;
        id|-id)          read_id=$value;;
        *)               echo "I dont know what to do with this"
   ease
done
0
terijo001