Contoh cara menggunakan getop di bash

345

Saya ingin memanggil myscriptfile dengan cara ini:

$ ./myscript -s 45 -p any_string

atau

$ ./myscript -h  #should display help
$ ./myscript     #should display help

Persyaratan saya adalah:

  • getopt di sini untuk mendapatkan argumen masukan
  • periksa yang -sada, jika tidak kembalikan kesalahan
  • periksa apakah nilainya setelah -s45 atau 90
  • periksa apakah -pada dan setelah itu ada string input
  • jika pengguna masuk ./myscript -hatau baru saja ./myscriptmenampilkan bantuan

Saya mencoba sejauh ini kode ini:

#!/bin/bash
while getopts "h:s:" arg; do
  case $arg in
    h)
      echo "usage" 
      ;;
    s)
      strength=$OPTARG
      echo $strength
      ;;
  esac
done

Tetapi dengan kode itu saya mendapatkan kesalahan. Bagaimana cara melakukannya dengan Bash dan getopt?

MOHAMED
sumber
2
Opsi seharusnya bersifat opsional. Jika Anda memerlukan nilai yang ditentukan oleh -s, membuat argumen posisi: ./myscript 45 anystring.
chepner
@chepner$./myscript -s 45 -p any_string
MOHAMED
Tidak apa-apa jika -psebenarnya merupakan opsi (yaitu, program Anda dapat melanjutkan jika tidak ada). Dalam hal ini ./myscript 45 -p any_string,. (Saya pikir itu getoptdapat menangani opsi campuran dan argumen posisi, sedangkan bashperintah getopts
bawaan

Jawaban:

513
#!/bin/bash

usage() { echo "Usage: $0 [-s <45|90>] [-p <string>]" 1>&2; exit 1; }

while getopts ":s:p:" o; do
    case "${o}" in
        s)
            s=${OPTARG}
            ((s == 45 || s == 90)) || usage
            ;;
        p)
            p=${OPTARG}
            ;;
        *)
            usage
            ;;
    esac
done
shift $((OPTIND-1))

if [ -z "${s}" ] || [ -z "${p}" ]; then
    usage
fi

echo "s = ${s}"
echo "p = ${p}"

Contoh berjalan:

$ ./myscript.sh
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -h
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s "" -p ""
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 10 -p foo
Usage: ./myscript.sh [-s <45|90>] [-p <string>]

$ ./myscript.sh -s 45 -p foo
s = 45
p = foo

$ ./myscript.sh -s 90 -p bar
s = 90
p = bar
Adrian Frühwirth
sumber
19
Dalam panggilan getopts, mengapa ada usus besar terkemuka? Kapan "h" memiliki titik dua setelahnya?
e40
7
Haruskah usage()benar-benar mengembalikan 1?
Pithikos
6
@Pithikos Poin bagus. Akal sehat mengatakan kepada saya bahwa ketika dipanggil via -hharus kembali 0, setelah memukul bendera yang tidak ada itu harus kembali >0(demi kesederhanaan saya tidak membedakan antara kasus-kasus dan tidak ada yang memaksa Anda untuk mencetak teks penggunaan dalam kasus terakhir) . Saya telah melihat program yang selalu kembali != 0, meskipun, bahkan pada -h/--help. Mungkin saya harus memperbarui snippet kalau-kalau orang menggunakan ini sebagai boilerplate (saya harap tidak)?
Adrian Frühwirth
1
@ A.Danischewski Ini dengan getoptsdesain ( '), tidak ada yang namanya "argumen opsional" dengan getopts. Parser tidak bisa tahu apakah token berikutnya adalah argumen untuk opsi saat ini atau opsi sendiri karena -pmungkin nilai yang dimaksud. Anda dapat meretas ini jika Anda benar-benar tahu bahwa parameter opsi tidak terlihat seperti opsi lain yang valid, ya, tetapi orang bisa mengatakan bahwa ada alasan argumen opsional tidak didefinisikan dalam POSIX.
Adrian Frühwirth
4
@ user1011471 Anda benar! Jadi, kurung kurawal, hanya membantu bashlexer dalam mengidentifikasi variabel. Mereka dalam banyak kasus tidak dibutuhkan dan fakta bahwa saya selalu menggunakannya hanyalah masalah gaya pengkodean pribadi. Bagi saya, lebih mudah (dan lebih cantik) untuk selalu menggunakannya daripada mengingat aturan parsing sehubungan dengan ambiguitas. Hampir sama mengapa seseorang akan menulis if (foo) { bar; }alih-alih if (foo) bar;dalam bahasa C-style (estetika dan / atau menghindari kesalahan konyol).
Adrian Frühwirth
109

Masalah dengan kode asli adalah:

  • h:mengharapkan parameter di mana seharusnya tidak, jadi ubah menjadi adil h(tanpa titik dua)
  • untuk mengharapkan -p any_string, Anda perlu menambahkan p:ke daftar argumen

Pada dasarnya :setelah opsi berarti membutuhkan argumen.


Sintaks dasarnya getoptsadalah (lihat man bash:):

getopts OPTSTRING VARNAME [ARGS...]

dimana:

  • OPTSTRING adalah string dengan daftar argumen yang diharapkan,

    • h- periksa opsi -h tanpa parameter; memberikan kesalahan pada opsi yang tidak didukung;
    • h:- periksa opsi -h dengan parameter; memberikan kesalahan pada opsi yang tidak didukung;
    • abc- cek untuk opsi -a, -b, -c; memberikan kesalahan pada opsi yang tidak didukung;
    • :abc- cek untuk opsi -a, -b, -c; membungkam kesalahan pada opsi yang tidak didukung;

      Catatan: Dengan kata lain, titik dua di depan opsi memungkinkan Anda menangani kesalahan dalam kode Anda. Variabel akan berisi ?dalam kasus opsi yang tidak didukung, :dalam kasus nilai yang hilang.

  • OPTARG - diatur ke nilai argumen saat ini,

  • OPTERR - menunjukkan apakah Bash akan menampilkan pesan kesalahan.

Jadi kodenya bisa:

#!/usr/bin/env bash
usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; }
[ $# -eq 0 ] && usage
while getopts ":hs:p:" arg; do
  case $arg in
    p) # Specify p value.
      echo "p is ${OPTARG}"
      ;;
    s) # Specify strength, either 45 or 90.
      strength=${OPTARG}
      [ $strength -eq 45 -o $strength -eq 90 ] \
        && echo "Strength is $strength." \
        || echo "Strength needs to be either 45 or 90, $strength found instead."
      ;;
    h | *) # Display help.
      usage
      exit 0
      ;;
  esac
done

Contoh penggunaan:

$ ./foo.sh 
./foo.sh usage:
    p) # Specify p value.
    s) # Specify strength, either 45 or 90.
    h | *) # Display help.
$ ./foo.sh -s 123 -p any_string
Strength needs to be either 45 or 90, 123 found instead.
p is any_string
$ ./foo.sh -s 90 -p any_string
Strength is 90.
p is any_string

Lihat: Tutorial getopts kecil di Bash Hackers Wiki

kenorb
sumber
2
Mengubah fungsi penggunaan untuk ini: usage() { echo "$0 usage:" && grep "[[:space:]].)\ #" $0 | sed 's/#//' | sed -r 's/([a-z])\)/-\1/'; exit 0; }. Itu hanya menyumbang satu karakter spasi putih sebelum opsi huruf, menghapus # dari komentar dan menambahkan '-' sebelum opsi huruf membuatnya lebih jelas untuk perintah.
poagester
2
@kenorb: Usus besar di depan opsi tidak mengabaikan opsi yang tidak didukung tetapi membungkam kesalahan dari bash dan memungkinkan Anda menanganinya dalam kode Anda. Variabel akan berisi '?' dalam hal opsi yang tidak didukung dan ':' dalam kasus nilai yang hilang.
Hynek -Pichi- Vychodil
1
Terima kasih atas dokumen terperinci, tidak dapat memperoleh :hak sampai saya melihat catatan ini. Kita perlu menambahkan :opsi ke mana kita mengharapkan argumen.
Aukhan
51

Menggunakan getopt

Mengapa getopt?

Untuk menguraikan argumen baris perintah yang diuraikan untuk menghindari kebingungan dan memperjelas opsi yang kami parsing sehingga pembaca perintah dapat memahami apa yang terjadi.

Apa itu getopt?

getoptdigunakan untuk memecah opsi (parse) di baris perintah untuk penguraian yang mudah dengan prosedur shell, dan untuk memeriksa opsi hukum. Ia menggunakan getopt(3)rutinitas GNU untuk melakukan ini.

getopt dapat memiliki jenis opsi berikut.

  1. Opsi tanpa nilai
  2. opsi pasangan kunci-nilai

Catatan: Dalam dokumen ini, saat menjelaskan sintaks:

  • Apa pun di dalam [] adalah parameter opsional dalam sintaks / contoh.
  • adalah pemegang tempat, yang berarti harus diganti dengan nilai aktual.

BAGAIMANA CARA MENGGUNAKAN getopt?

Sintaks: Bentuk Pertama

getopt optstring parameters

Contoh:

# This is correct
getopt "hv:t::" "-v 123 -t123"  
getopt "hv:t::" "-v123 -t123"  # -v and 123 doesn't have whitespace

# -h takes no value.
getopt "hv:t::" "-h -v123"


# This is wrong. after -t can't have whitespace.
# Only optional params cannot have whitespace between key and value
getopt "hv:t::" "-v 123 -t 123"

# Multiple arguments that takes value.
getopt "h:v:t::g::" "-h abc -v 123 -t21"

# Multiple arguments without value
# All of these are correct
getopt "hvt" "-htv"
getopt "hvt" "-h -t -v"
getopt "hvt" "-tv -h"

Di sini h, v, t adalah opsi dan -h -v -t adalah bagaimana opsi harus diberikan dalam command-line.

  1. 'h' adalah opsi tanpa nilai.
  2. 'v:' menyiratkan bahwa opsi -v memiliki nilai dan merupakan opsi wajib. ':' berarti memiliki nilai.
  3. 't ::' menyiratkan bahwa opsi -t memiliki nilai tetapi opsional. '::' berarti opsional.

Dalam param opsional, nilai tidak dapat memiliki pemisahan spasi dengan opsi. Jadi, dalam contoh "-t123", -t adalah opsi 123 adalah nilai.

Sintaks: Bentuk Kedua

getopt [getopt_options] [--] [optstring] [parameters]

Di sini setelah getopt dibagi menjadi lima bagian

  • Perintah itu sendiri yaitu getopt
  • Opsi getopt_options, menjelaskan cara mem-parsing argumen. opsi panjang dasbor tunggal, opsi dasbor ganda.
  • -, pisahkan getopt_options dari opsi yang ingin Anda parsing dan opsi pendek yang diizinkan
  • Opsi singkat, diambil segera setelah - ditemukan. Sama seperti sintaks Formulir pertama.
  • Parameter, ini adalah opsi yang telah Anda masukkan ke dalam program. Opsi yang Anda ingin parse dan menetapkan nilai aktualnya.

Contohnya

getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"

Sintaks: Bentuk Ketiga

getopt [getopt_options] [-o options] [--] [optstring] [parameters]

Di sini setelah getopt dibagi menjadi lima bagian

  • Perintah itu sendiri yaitu getopt
  • Opsi getopt_options, menjelaskan cara mem-parsing argumen. opsi panjang dasbor tunggal, opsi dasbor ganda.
  • Opsi singkat yaitu -o atau --opsi. Sama seperti sintaks Formulir pertama tetapi dengan opsi "-o" dan sebelum "-" (tanda hubung ganda).
  • -, pisahkan getopt_options dari opsi yang ingin Anda parsing dan opsi pendek yang diizinkan
  • Parameter, ini adalah opsi yang telah Anda masukkan ke dalam program. Opsi yang Anda ingin parse dan menetapkan nilai aktualnya.

Contohnya

getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"

GETOPT_OPTIONS

getopt_options mengubah cara params baris perintah diuraikan.

Berikut adalah beberapa getopt_options

Opsi: -l atau --pilihan panjang

Perintah getopt berarti harus memungkinkan opsi multi-karakter dikenali. Beberapa opsi dipisahkan dengan koma.

Misalnya, --name=Karthikadalah opsi panjang yang dikirim dalam baris perintah. Dalam getopt, penggunaan opsi panjang seperti

getopt "name:,version" "--name=Karthik"

Karena nama: ditentukan, opsi harus mengandung nilai

Opsi: -a atau --alternatif

Berarti perintah getopt harus memungkinkan opsi panjang untuk memiliki satu tanda hubung '-' daripada tanda hubung ganda '-'.

Contoh, alih-alih --name=KarthikAnda bisa menggunakan saja-name=Karthik

getopt "name:,version" "-name=Karthik"

Contoh skrip lengkap dengan kode:

#!/bin/bash

# filename: commandLine.sh
# author: @theBuzzyCoder

showHelp() {
# `cat << EOF` This means that cat should stop reading when EOF is detected
cat << EOF  
Usage: ./installer -v <espo-version> [-hrV]
Install Pre-requisites for EspoCRM with docker in Development mode

-h, -help,          --help                  Display help

-v, -espo-version,  --espo-version          Set and Download specific version of EspoCRM

-r, -rebuild,       --rebuild               Rebuild php vendor directory using composer and compiled css using grunt

-V, -verbose,       --verbose               Run script in verbose mode. Will print out each step of execution.

EOF
# EOF is found above and hence cat command stops reading. This is equivalent to echo but much neater when printing out.
}


export version=0
export verbose=0
export rebuilt=0

# $@ is all command line parameters passed to the script.
# -o is for short options like -v
# -l is for long options with double dash like --version
# the comma separates different long options
# -a is for long options with single dash like -version
options=$(getopt -l "help,version:,verbose,rebuild,dryrun" -o "hv:Vrd" -a -- "$@")

# set --:
# If no arguments follow this option, then the positional parameters are unset. Otherwise, the positional parameters 
# are set to the arguments, even if some of them begin with a ‘-’.
eval set -- "$options"

while true
do
case $1 in
-h|--help) 
    showHelp
    exit 0
    ;;
-v|--version) 
    shift
    export version=$1
    ;;
-V|--verbose)
    export verbose=1
    set -xv  # Set xtrace and verbose mode.
    ;;
-r|--rebuild)
    export rebuild=1
    ;;
--)
    shift
    break;;
esac
shift
done

Menjalankan file skrip ini:

# With short options grouped together and long option
# With double dash '--version'

bash commandLine.sh --version=1.0 -rV
# With short options grouped together and long option
# With single dash '-version'

bash commandLine.sh -version=1.0 -rV

# OR with short option that takes value, value separated by whitespace
# by key

bash commandLine.sh -v 1.0 -rV

# OR with short option that takes value, value without whitespace
# separation from key.

bash commandLine.sh -v1.0 -rV

# OR Separating individual short options

bash commandLine.sh -v1.0 -r -V
theBuzzyCoder
sumber
sumber: linkedin.com/pulse/…
theBuzzyCoder
getopt vs getopts .. kepatuhan lintas platform yang sangat berbeda
shadowbq
35

Contoh yang dikemas dengan getopt(distro saya memasukkannya ke dalam /usr/share/getopt/getopt-parse.bash) sepertinya mencakup semua kasus Anda:

#!/bin/bash

# A small example program for using the new getopt(1) program.
# This program will only work with bash(1)
# An similar program using the tcsh(1) script language can be found
# as parse.tcsh

# Example input and output (from the bash prompt):
# ./parse.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument `more'
# Option b, argument ` very long '
# Remaining arguments:
# --> `par1'
# --> `another arg'
# --> `wow!*\?'

# Note that we use `"$@"' to let each command-line parameter expand to a 
# separate word. The quotes around `$@' are essential!
# We need TEMP as the `eval set --' would nuke the return value of getopt.
TEMP=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \
     -n 'example.bash' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -a|--a-long) echo "Option a" ; shift ;;
        -b|--b-long) echo "Option b, argument \`$2'" ; shift 2 ;;
        -c|--c-long) 
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                "") echo "Option c, no argument"; shift 2 ;;
                *)  echo "Option c, argument \`$2'" ; shift 2 ;;
            esac ;;
        --) shift ; break ;;
        *) echo "Internal error!" ; exit 1 ;;
    esac
done
echo "Remaining arguments:"
for arg do echo '--> '"\`$arg'" ; done
Brian Cain
sumber
11
Perintah eksternal getopt (1) tidak pernah aman untuk digunakan, kecuali Anda tahu itu adalah GNU getopt, Anda menyebutnya dengan cara spesifik GNU, dan Anda memastikan bahwa GETOPT_COMPATIBLE tidak ada di lingkungan. Gunakan getopts (builtin shell) sebagai gantinya, atau cukup lompati parameter posisi.
Gilles Quenot
@sputnick, tyvm, tidak tahu ini.
Brian Cain
14
Eh, tidak ada perintah eksternal yang aman digunakan oleh standar itu. Built-in getopts tidak memiliki fitur penting dan jika Anda ingin memeriksa GETOPT_COMPATIBLE, itu lebih mudah daripada mem-porting fitur getopt.
Michael Terry
12

Saya tahu bahwa ini sudah dijawab, tetapi untuk catatan dan untuk siapa pun dengan permintaan yang sama dengan saya, saya memutuskan untuk mengirim jawaban terkait ini. Kode dibanjiri dengan komentar untuk menjelaskan kode.

Jawaban yang diperbarui:

Simpan file sebagai getopt.sh:

#!/bin/bash

function get_variable_name_for_option {
    local OPT_DESC=${1}
    local OPTION=${2}
    local VAR=$(echo ${OPT_DESC} | sed -e "s/.*\[\?-${OPTION} \([A-Z_]\+\).*/\1/g" -e "s/.*\[\?-\(${OPTION}\).*/\1FLAG/g")

    if [[ "${VAR}" == "${1}" ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

function parse_options {
    local OPT_DESC=${1}
    local INPUT=$(get_input_for_getopts "${OPT_DESC}")

    shift
    while getopts ${INPUT} OPTION ${@};
    do
        [ ${OPTION} == "?" ] && usage
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
            [ "${VARNAME}" != "" ] && eval "${VARNAME}=${OPTARG:-true}" # && printf "\t%s\n" "* Declaring ${VARNAME}=${!VARNAME} -- OPTIONS='$OPTION'"
    done

    check_for_required "${OPT_DESC}"

}

function check_for_required {
    local OPT_DESC=${1}
    local REQUIRED=$(get_required "${OPT_DESC}" | sed -e "s/\://g")
    while test -n "${REQUIRED}"; do
        OPTION=${REQUIRED:0:1}
        VARNAME=$(get_variable_name_for_option "${OPT_DESC}" "${OPTION}")
                [ -z "${!VARNAME}" ] && printf "ERROR: %s\n" "Option -${OPTION} must been set." && usage
        REQUIRED=${REQUIRED:1}
    done
}

function get_input_for_getopts {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_optional {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/[^[]*\(\[[^]]*\]\)[^[]*/\1/g" -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/[][ -]//g"
}

function get_required {
    local OPT_DESC=${1}
    echo ${OPT_DESC} | sed -e "s/\([a-zA-Z]\) [A-Z_]\+/\1:/g" -e "s/\[[^[]*\]//g" -e "s/[][ -]//g"
}

function usage {
    printf "Usage:\n\t%s\n" "${0} ${OPT_DESC}"
    exit 10
}

Maka Anda dapat menggunakannya seperti ini:

#!/bin/bash
#
# [ and ] defines optional arguments
#

# location to getopts.sh file
source ./getopt.sh
USAGE="-u USER -d DATABASE -p PASS -s SID [ -a START_DATE_TIME ]"
parse_options "${USAGE}" ${@}

echo ${USER}
echo ${START_DATE_TIME}

Jawaban lama:

Saya baru-baru ini perlu menggunakan pendekatan generik. Saya menemukan solusi ini:

#!/bin/bash
# Option Description:
# -------------------
#
# Option description is based on getopts bash builtin. The description adds a variable name feature to be used
# on future checks for required or optional values.
# The option description adds "=>VARIABLE_NAME" string. Variable name should be UPPERCASE. Valid characters
# are [A-Z_]*.
#
# A option description example:
#   OPT_DESC="a:=>A_VARIABLE|b:=>B_VARIABLE|c=>C_VARIABLE"
#
# -a option will require a value (the colon means that) and should be saved in variable A_VARIABLE.
# "|" is used to separate options description.
# -b option rule applies the same as -a.
# -c option doesn't require a value (the colon absense means that) and its existence should be set in C_VARIABLE
#
#   ~$ echo get_options ${OPT_DESC}
#   a:b:c
#   ~$
#


# Required options 
REQUIRED_DESC="a:=>REQ_A_VAR_VALUE|B:=>REQ_B_VAR_VALUE|c=>REQ_C_VAR_FLAG"

# Optional options (duh)
OPTIONAL_DESC="P:=>OPT_P_VAR_VALUE|r=>OPT_R_VAR_FLAG"

function usage {
    IFS="|"
    printf "%s" ${0}
    for i in ${REQUIRED_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
    printf " %s" "-${i:0:1} $VARNAME"
    done

    for i in ${OPTIONAL_DESC};
    do
        VARNAME=$(echo $i | sed -e "s/.*=>//g")
        printf " %s" "[-${i:0:1} $VARNAME]"
    done
    printf "\n"
    unset IFS
    exit
}

# Auxiliary function that returns options characters to be passed
# into 'getopts' from a option description.
# Arguments:
#   $1: The options description (SEE TOP)
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   OPTIONS=$(get_options ${OPT_DESC})
#   echo "${OPTIONS}"
#
# Output:
#   "h:f:PW"
function get_options {
    echo ${1} | sed -e "s/\([a-zA-Z]\:\?\)=>[A-Z_]*|\?/\1/g"
}

# Auxiliary function that returns all variable names separated by '|'
# Arguments:
#       $1: The options description (SEE TOP)
#
# Example:
#       OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#       VARNAMES=$(get_values ${OPT_DESC})
#       echo "${VARNAMES}"
#
# Output:
#       "H_VAR|F_VAR|P_VAR|W_VAR"
function get_variables {
    echo ${1} | sed -e "s/[a-zA-Z]\:\?=>\([^|]*\)/\1/g"
}

# Auxiliary function that returns the variable name based on the
# option passed by.
# Arguments:
#   $1: The options description (SEE TOP)
#   $2: The option which the variable name wants to be retrieved
#
# Example:
#   OPT_DESC="h:=>H_VAR|f:=>F_VAR|P=>P_VAR|W=>W_VAR"
#   H_VAR=$(get_variable_name ${OPT_DESC} "h")
#   echo "${H_VAR}"
#
# Output:
#   "H_VAR"
function get_variable_name {
    VAR=$(echo ${1} | sed -e "s/.*${2}\:\?=>\([^|]*\).*/\1/g")
    if [[ ${VAR} == ${1} ]]; then
        echo ""
    else
        echo ${VAR}
    fi
}

# Gets the required options from the required description
REQUIRED=$(get_options ${REQUIRED_DESC})

# Gets the optional options (duh) from the optional description
OPTIONAL=$(get_options ${OPTIONAL_DESC})

# or... $(get_options "${OPTIONAL_DESC}|${REQUIRED_DESC}")

# The colon at starts instructs getopts to remain silent
while getopts ":${REQUIRED}${OPTIONAL}" OPTION
do
    [[ ${OPTION} == ":" ]] && usage
    VAR=$(get_variable_name "${REQUIRED_DESC}|${OPTIONAL_DESC}" ${OPTION})
    [[ -n ${VAR} ]] && eval "$VAR=${OPTARG}"
done

shift $(($OPTIND - 1))

# Checks for required options. Report an error and exits if
# required options are missing.

# Using function version ...
VARS=$(get_variables ${REQUIRED_DESC})
IFS="|"
for VARNAME in $VARS;
do
    [[ -v ${VARNAME} ]] || usage
done
unset IFS

# ... or using IFS Version (no function)
OLDIFS=${IFS}
IFS="|"
for i in ${REQUIRED_DESC};
do
    VARNAME=$(echo $i | sed -e "s/.*=>//g")
    [[ -v ${VARNAME} ]] || usage
    printf "%s %s %s\n" "-${i:0:1}" "${!VARNAME:=present}" "${VARNAME}"
done
IFS=${OLDIFS}

Saya tidak menguji ini secara kasar, jadi saya bisa memiliki beberapa bug di sana.

Sebastian
sumber
1
Jika Anda menggunakan getoptsfungsi, tambahkan local OPTIND OPTARGke fungsi
glenn jackman
@glennjackman sebenarnya lebih seperti pendekatan sed daripada menggunakangetopts
Sebastian
8

Contoh 7 POSIX

Anda juga perlu memeriksa contoh dari standar: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html

aflag=
bflag=
while getopts ab: name
do
    case $name in
    a)    aflag=1;;
    b)    bflag=1
          bval="$OPTARG";;
    ?)   printf "Usage: %s: [-a] [-b value] args\n" $0
          exit 2;;
    esac
done
if [ ! -z "$aflag" ]; then
    printf "Option -a specified\n"
fi
if [ ! -z "$bflag" ]; then
    printf 'Option -b "%s" specified\n' "$bval"
fi
shift $(($OPTIND - 1))
printf "Remaining arguments are: %s\n" "$*"

Dan kemudian kita bisa mencobanya:

$ sh a.sh
Remaining arguments are: 
$ sh a.sh -a
Option -a specified
Remaining arguments are: 
$ sh a.sh -b
No arg for -b option
Usage: a.sh: [-a] [-b value] args
$ sh a.sh -b myval
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh -a -b myval
Option -a specified
Option -b "myval" specified
Remaining arguments are: 
$ sh a.sh remain
Remaining arguments are: remain
$ sh a.sh -- -a remain
Remaining arguments are: -a remain

Diuji di Ubuntu 17.10, shadalah tanda hubung 0.5.8.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
sumber
0

"Getops" dan "getopt" sangat terbatas. Sementara "getopt" disarankan untuk tidak digunakan sama sekali, itu memang menawarkan opsi panjang. Sedangkan "getopts" hanya memungkinkan opsi karakter tunggal seperti "-a" "-b". Ada beberapa kelemahan lagi saat menggunakan salah satunya.

Jadi saya sudah menulis skrip kecil yang menggantikan "getopts" dan "getopt". Ini awal, mungkin banyak yang bisa diperbaiki.

Pembaruan 08-04-2020 : Saya telah menambahkan dukungan untuk tanda hubung mis. "--Package-name".

Penggunaan: "./script.sh paket instal --package" name with space "--build --archive"

# Example:
# parseArguments "${@}"
# echo "${ARG_0}" -> package
# echo "${ARG_1}" -> install
# echo "${ARG_PACKAGE}" -> "name with space"
# echo "${ARG_BUILD}" -> 1 (true)
# echo "${ARG_ARCHIVE}" -> 1 (true)
function parseArguments() {
  PREVIOUS_ITEM=''
  COUNT=0
  for CURRENT_ITEM in "${@}"
  do
    if [[ ${CURRENT_ITEM} == "--"* ]]; then
      printf -v "ARG_$(formatArgument "${CURRENT_ITEM}")" "%s" "1" # could set this to empty string and check with [ -z "${ARG_ITEM-x}" ] if it's set, but empty.
    else
      if [[ $PREVIOUS_ITEM == "--"* ]]; then
        printf -v "ARG_$(formatArgument "${PREVIOUS_ITEM}")" "%s" "${CURRENT_ITEM}"
      else
        printf -v "ARG_${COUNT}" "%s" "${CURRENT_ITEM}"
      fi
    fi

    PREVIOUS_ITEM="${CURRENT_ITEM}"
    (( COUNT++ ))
  done
}

# Format argument.
function formatArgument() {
  ARGUMENT="${1^^}" # Capitalize.
  ARGUMENT="${ARGUMENT/--/}" # Remove "--".
  ARGUMENT="${ARGUMENT//-/_}" # Replace "-" with "_".
  echo "${ARGUMENT}"
}
Twoez
sumber