bash मैं बैश में कमांड लाइन तर्क कैसे पार्स करूं?




command-line scripting (23)

मेरा जवाब काफी हद तक ब्रूनो ब्रोनोस्की के जवाब पर आधारित है, लेकिन मैंने अपने दो शुद्ध बैश कार्यान्वयनों को एक तरह से मैश किया है जिसे मैं अक्सर उपयोग करता हूं।

# 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

यह आपको दोनों जगह अलग-अलग विकल्प / मान, साथ ही बराबर परिभाषित मानों की अनुमति देता है।

तो आप अपनी स्क्रिप्ट का उपयोग कर चला सकते हैं:

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

साथ ही साथ:

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

और दोनों के पास एक ही अंतिम परिणाम होना चाहिए।

पेशेवरों:

  • दोनों -र्ग = मान और -र्ग मान दोनों की अनुमति देता है

  • किसी भी तर्क नाम के साथ काम करता है जिसे आप बैश में उपयोग कर सकते हैं

    • मतलब-ए या -र्ग या --र्ग या -र्ग या जो भी हो
  • शुद्ध बाश Getopt या getopts सीखने / उपयोग करने की कोई ज़रूरत नहीं है

कान्स:

  • तर्क गठबंधन नहीं कर सकते हैं

    • मतलब नो-एबीसी। आपको -a -b -c करना होगा

ये एकमात्र पेशेवर / विपक्ष हैं जो मैं अपने सिर के ऊपर से सोच सकता हूं

कहो, मेरे पास एक स्क्रिप्ट है जिसे इस पंक्ति के साथ बुलाया जाता है:

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

या यह एक:

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

इस तरह का विश्लेषण करने का स्वीकार्य तरीका क्या है कि प्रत्येक मामले में (या दोनों के कुछ संयोजन) $v , $f , और $d सभी को true सेट किया जाएगा और $outFile /fizz/someOtherFile बराबर होगा?


कोई जवाब बढ़ाया getopt का उल्लेख नहीं है। और शीर्ष वोट वाला उत्तर भ्रामक है: यह अनदेखा करता -⁠vfd शैली शैली छोटे विकल्प (ओपी द्वारा अनुरोध किया गया), -⁠vfd तर्कों के बाद विकल्प (ओपी द्वारा भी अनुरोध किया गया) और यह पार्सिंग-त्रुटियों को अनदेखा करता है। बजाय:

  • उपयोग-लिनक्स या पूर्व में जीएनयू getopt से उन्नत getopt उपयोग करें1
  • यह getopt_long() जीएनयू glibc के सी समारोह के साथ काम करता है।
  • क्या सभी उपयोगी विशिष्ट विशेषताएं हैं (दूसरों के पास नहीं है):
    • तर्कों को संभालने, वर्णों को उद्धृत करने और तर्क 2 में बाइनरी भी संभालता है
    • यह अंत में विकल्पों को संभाल सकता है: script.sh -o outFile file1 file2 -v
    • = script.sh --outfile=fileOut --infile fileIn लंबे विकल्प की अनुमति देता है: script.sh --outfile=fileOut --infile fileIn
  • इतनी पुरानी है कि पहले से ही 3 जीएनयू सिस्टम में यह गुम है (उदाहरण के लिए किसी भी लिनक्स में यह है)।
  • आप इसके अस्तित्व के लिए परीक्षण कर सकते हैं: getopt --test → वापसी मान 4।
  • अन्य getopts या शेल- getopts सीमित उपयोग के हैं।

निम्नलिखित कॉल

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

सभी वापसी

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

निम्नलिखित myscript

#!/bin/bash

getopt --test > /dev/null
if [[ $? -ne 4 ]]; then
    echo "I’m sorry, `getopt --test` failed in this environment."
    exit 1
fi

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

# -temporarily store output to be able to check for errors
# -e.g. use “--options” parameter by name to activate quoting/enhanced mode
# -pass arguments only via   -- "[email protected]"   to separate them correctly
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTIONS --name "$0" -- "[email protected]")
if [[ $? -ne 0 ]]; then
    # e.g. $? == 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"

# 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 बढ़ाया गेटोपट उपलब्ध है; ओएस एक्स पर gnu-getopt स्थापित करने का प्रयास करें
2 POSIX exec() सम्मेलनों में कमांड लाइन तर्कों में बाइनरी न्यूल को पास करने का कोई विश्वसनीय तरीका नहीं है; उन बाइट्स समय से पहले तर्क खत्म करते हैं
1 99 7 या उससे पहले जारी 3 पहला संस्करण (मैंने इसे केवल 1 99 7 तक ट्रैक किया था)


इस तरह मैं एक समारोह में करता हूं ताकि एक ही समय में स्टैक में कहीं अधिक हो जाने वाले गेटों को तोड़ने से बचें:

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
...
}

यह जानने के लिए भी उपयोगी हो सकता है, आप एक मूल्य निर्धारित कर सकते हैं और यदि कोई इनपुट प्रदान करता है, तो उस मान के साथ डिफ़ॉल्ट को ओवरराइड करें ..

myscript.sh -f ./serverlist.txt या बस ./myscript.sh (और यह डिफ़ॉल्ट लेता है)

    #!/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}"

Assume we create a shell script named test_args.sh as follow

#!/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"

After we run the following command:

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

The output would be:

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

मुझे इस मामले को स्क्रिप्ट में पोर्टेबल पार्सिंग लिखने के लिए मिला है, इसलिए निराशाजनक है कि मैंने Argbash - एक FOSS कोड जनरेटर लिखा है जो आपकी स्क्रिप्ट के लिए तर्क- Argbash कोड उत्पन्न कर सकता है और इसमें कुछ अच्छी विशेषताएं हैं:

https://argbash.io


EasyOptions को किसी भी पार्सिंग की आवश्यकता नहीं है:

## 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

अनदेखा करने के लिए एक और उदाहरण जोड़ने के जोखिम पर, मेरी योजना है।

  • हैंडल -n arg और --name=arg
  • अंत में तर्क की अनुमति देता है
  • यदि कुछ भी गलत वर्तनी है तो सेन त्रुटियों को दिखाता है
  • संगत, bashisms का उपयोग नहीं करता है
  • पठनीय, एक लूप में राज्य को बनाए रखने की आवश्यकता नहीं है

उम्मीद है कि यह किसी के लिए उपयोगी है।

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

Solution that preserves unhandled arguments. Demos Included.

Here is my solution. It is VERY flexible and unlike others, shouldn't require external packages and handles leftover arguments cleanly.

Usage is: ./myscript -flag flagvariable -otherflag flagvar2

All you have to do is edit the validflags line. It prepends a hyphen and searches all arguments. It then defines the next argument as the flag name eg

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

The main code (short version, verbose with examples further down, also a version with erroring out):

#!/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

The verbose version with built in echo demos:

#!/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

Final one, this one errors out if an invalid -argument is passed through.

#!/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

Pros: What it does, it handles very well. It preserves unused arguments which a lot of the other solutions here don't. It also allows for variables to be called without being defined by hand in the script. It also allows prepopulation of variables if no corresponding argument is given. (See verbose example).

Cons: Can't parse a single complex arg string eg -xcvf would process as a single argument. You could somewhat easily write additional code into mine that adds this functionality though.


The top answer to this question seemed a bit buggy when I tried it -- here's my solution which I've found to be more robust:

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

This example shows how to use getopt and eval and HEREDOC and shift to handle short and long parameters with and without a required value that follows. Also the switch/case statement is concise and easy to follow.

#!/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

The most significant lines of the script above are these:

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

Short, to the point, readable, and handles just about everything (IMHO).

Hope that helps someone.


I have write a bash helper to write a nice bash tool

project home: https://gitlab.mbedsys.org/mbedsys/bashopts

उदाहरण:

#!/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

will give help:

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:"")

enjoy :)


मैं इस सवाल के करीब 4 साल देर से हूं, लेकिन वापस देना चाहता हूं। मैंने अपने पुराने एडहोक परम पार्सिंग को साफ करने के लिए शुरुआती बिंदु के रूप में पहले के उत्तरों का उपयोग किया था। मैंने फिर निम्नलिखित टेम्पलेट कोड का पुन: उपयोग किया। यह = या स्पेस से अलग तर्कों का उपयोग करते हुए, साथ ही कई छोटे पैरा को एक साथ समूहीकृत करते हुए, लंबे और छोटे पैरा दोनों को संभालता है। आखिरकार यह किसी भी गैर-परम तर्क को $ 1, $ 2 में फिर से सम्मिलित करता है .. चर। मुझे उम्मीद है कि यह उपयोगी है।

#!/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

मैं विकल्प पार्सिंग का अपना संस्करण प्रस्तुत करना चाहता हूं, जो निम्न के लिए अनुमति देता है:

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

इसके लिए भी अनुमति देता है (अवांछित हो सकता है):

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

यदि किसी विकल्प पर उपयोग किया जाना है या नहीं, तो आपको उपयोग से पहले निर्णय लेना होगा। यह कोड को साफ रखना है (आईएसएच)।

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

Here is my approach - using regexp.

  • no getopts
  • it handles block of short parameters -qwerty
  • it handles short parameters -q -w -e
  • it handles long options --qwerty
  • you can pass attribute to short or long option (if you are using block of short options, attribute is attached to the last option)
  • you can use spaces or = to provide attributes, but attribute matches until encountering hyphen+space "delimiter", so in --q=qwe ty qwe ty is one attribute
  • it handles mix of all above so -oa -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute is valid

script:

#!/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

Here is my improved solution of Bruno Bronosky's answer using variable arrays.

it lets you mix parameters position and give you a parameter array preserving the order without the options

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

Will output for example:

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

पसंदीदा विधि: गेटोपट के बिना सीधे बैश का उपयोग करना

ओपी ने पूछा कि मैंने मूल रूप से सवाल का जवाब दिया था। यह क्यू / ए बहुत ध्यान दे रहा है, इसलिए मुझे ऐसा करने के लिए गैर-जादू मार्ग भी प्रदान करना चाहिए। मैं गंदे sed को ठीक करने के लिए Guneysus के जवाब पर विस्तार करने जा रहा हूं और Tobias Kienzler के सुझाव शामिल हैं

मुख्य मूल्य जोड़ी तर्क पास करने के दो सबसे आम तरीके हैं:

सीधे बैश स्पेस अलग

उपयोग ./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

सीधे बैश बराबर बराबर है

उपयोग ./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

${i#*=} इस मार्गदर्शिका में "सबस्ट्रिंग रिमूवल" के लिए खोज को बेहतर ढंग से समझने के लिए। यह कार्यात्मक रूप से `sed 's/[^=]*=//' <<< "$i"` जो एक आवश्यक उपप्रोसेसर या `echo "$i" | sed 's/[^=]*=//'` कहता है `echo "$i" | sed 's/[^=]*=//'` `echo "$i" | sed 's/[^=]*=//'` जो दो आवश्यक उपप्रवाहों को बुलाता है।

गेटोपेट का उपयोग करना

से: http://mywiki.wooledge.org/BashFAQ/035#getopts

गेटोपेट (1) का कभी भी उपयोग न करें। getopt खाली तर्क तारों, या एम्बेडेड व्हाइटस्पेस के साथ तर्क संभाल नहीं कर सकता है। कृपया भूल जाओ कि यह कभी अस्तित्व में है।

POSIX खोल (और अन्य) getopts प्रदान करते हैं जो इसके बजाय उपयोग करने के लिए सुरक्षित है। यहां एक सरल getopts उदाहरण है:

#!/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 फायदे हैं:

  1. यह पोर्टेबल है, और डैश में काम करेगा।
  2. यह स्वचालित रूप से अपेक्षित यूनिक्स तरीके से -vf filename जैसी चीज़ों को संभाल सकता है।

getopts का नुकसान यह है कि यह केवल trickery के बिना छोटे विकल्प ( -h , नहीं --help ) संभाल सकता है।

एक गेट्स ट्यूटोरियल है जो बताता है कि सभी वाक्यविन्यास और चर का अर्थ क्या है। help getopts , help getopts में भी help getopts , जो सूचनात्मक हो सकती है।


ध्यान दें कि getopt(1) एटी एंड टी से एक छोटी सी गलती थी।

गेटोपेट 1 9 84 में बनाया गया था लेकिन 1 9 86 में पहले से ही दफनाया गया था क्योंकि यह वास्तव में उपयोगी नहीं था।

तथ्य यह है कि getopt बहुत पुराना है, यह है कि getopt(1) मैन पेज अभी भी "[email protected]" बजाय "$*" का उल्लेख करता है, जिसे 1 9 86 में बोर्न शैल में getopts(1) खोल निर्मित के साथ जोड़ा गया था अंदर रिक्त स्थान के साथ तर्क के साथ सौदा करने के लिए।

बीटीडब्लू: यदि आप शेल स्क्रिप्ट में लंबे विकल्पों को पार्स करने में रुचि रखते हैं, तो यह जानना ब्याज हो सकता है कि libc (Solaris) और ksh93 दोनों से getopt(3) कार्यान्वयन ने एक समान लंबा विकल्प कार्यान्वयन जोड़ा जो छोटे विकल्पों के रूप में लंबे विकल्पों का समर्थन करता है विकल्प। यह ksh93 और Bourne Shell को getopts माध्यम से लंबे विकल्पों के लिए एक समान इंटरफ़ेस लागू करने का कारण बनता है।

बोर्न शैल मैन पेज से लिया गया लंबे विकल्पों के लिए एक उदाहरण:

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

दिखाता है कि बोर्न शैल और ksh93 दोनों में कितने समय तक विकल्प उपनाम का उपयोग किया जा सकता है।

हाल ही में बोर्न शैल के मैन पेज को देखें:

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

और ओपनसोलालिस से गेटोपेट (3) के लिए मैन पेज:

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

और आखिरकार, पुरानी $ * सत्यापित करने के लिए getopt (1) मैन पेज:

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


getopts() getopt() / getopts() एक अच्छा विकल्प है। here से चोरी हुई:

"मिनीपेट" का सरल उपयोग इस मिनी-स्क्रिप्ट में दिखाया गया है:

#!/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

हमने जो कहा है वह है कि किसी भी, -b, -c या -d की अनुमति होगी, लेकिन उस-सी के बाद एक तर्क ("सी:" कहता है)।

अगर हम इसे "जी" कहते हैं और इसे आज़माएं:

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

हम दो तर्कों से शुरू करते हैं, और "गेटोपट" विकल्पों को अलग करता है और प्रत्येक को अपने तर्क में डाल देता है। यह भी "-" जोड़ा गया।


स्थितित्मक और ध्वज आधारित तर्क मिश्रण

--param = arg (सीमित समतुल्य)

स्थितित्मक तर्कों के बीच स्वतंत्र रूप से झंडे मिश्रण:

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

एक काफी संक्षिप्त दृष्टिकोण के साथ पूरा किया जा सकता है:

# 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 तर्क (अंतरिक्ष सीमित)

यह सामान्य रूप से स्पष्ट नहीं है - --flag=value और --flag value शैलियों को मिश्रण करने के लिए।

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

यह पढ़ने के लिए थोड़ा पासा है, लेकिन अभी भी मान्य है

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

स्रोत

# 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

मुझे लगता है कि यह उपयोग करने के लिए काफी आसान है:

#!/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

आमंत्रण उदाहरण:

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

गेटोपेट्स बहुत अच्छा काम करता है अगर # 1 आपने इसे इंस्टॉल किया है और # 2 आप इसे उसी मंच पर चलाने का इरादा रखते हैं। ओएसएक्स और लिनक्स (उदाहरण के लिए) इस संबंध में अलग-अलग व्यवहार करते हैं।

यहां एक (गैर गेटोप्स) समाधान है जो बराबर, गैर-बराबर और बूलियन झंडे का समर्थन करता है। उदाहरण के लिए आप अपनी स्क्रिप्ट इस तरह से चला सकते हैं:

./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

@ गुनीसस द्वारा उत्कृष्ट उत्तर पर विस्तार करते हुए, यहां एक चिमटा है जो उपयोगकर्ता को जो भी वाक्यविन्यास पसंद करता है, उसका उपयोग करने देता है, उदाहरण के लिए

command -x=myfilename.ext --another_switch 

बनाम

command -x myfilename.ext --another_switch

यही कहना है कि बराबर सफेद जगह के साथ प्रतिस्थापित किया जा सकता है।

यह "अस्पष्ट व्याख्या" आपकी पसंद के हिसाब से नहीं हो सकती है, लेकिन यदि आप ऐसी स्क्रिप्ट बना रहे हैं जो अन्य उपयोगिताओं के साथ अंतर-परिवर्तनीय हैं (जैसा कि मेरा मामला है, जो ffmpeg के साथ काम करना चाहिए), लचीलापन उपयोगी है।

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






arguments