script - Un ejemplo de cómo usar getopts en bash
script linux ejemplos (6)
El ejemplo empaquetado con getopt
(mi distribución lo puso en /usr/share/getopt/getopt-parse.bash
) parece que cubre todos sus casos:
#!/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
Quiero llamar myscript
archivo myscript
de esta manera:
$ ./myscript -s 45 -p any_string
o
$ ./myscript -h >>> should display help
$ ./myscript >>> should display help
Mis requerimientos son:
-
getopt
aquí para obtener los argumentos de entrada - compruebe que
-s
existe, si no devuelve error - compruebe que el valor después de la
-s
es 45 o 90 - comprueba que existe
-p
y que hay una cadena de entrada después de - si el usuario ingresa
./myscript -h
o simplemente./myscript
entonces muestra ayuda
He intentado hasta ahora este código:
#!/bin/bash
while getopts "h:s:" arg; do
case $arg in
h)
echo "usage"
;;
s)
strength=$OPTARG
echo $strength
;;
esac
done
Pero con ese código me sale errores. ¿Cómo hacerlo con Bash y getopt
?
El problema con el código original es que:
-
h:
espera el parámetro donde no debería, así que cámbielo a soloh
(sin dos puntos) - para esperar
-p any_string
, debe agregarp:
a la lista de argumentos
La sintaxis básica de getopts
es (ver: man bash
):
getopts OPTSTRING VARNAME [ARGS...]
dónde:
OPTSTRING
es una cadena con una lista de argumentos esperados,-
h
- verifique la opción-h
sin parámetros; da error en opciones no soportadas; -
h:
- Verifique la opción-h
con parámetro; da errores en opciones no soportadas; -
abc
- verifique las opciones-a
,-b
,-c
; da errores en opciones no soportadas; :abc
- verifique las opciones-a
,-b
,-c
; silencia los errores en opciones no soportadas;Notas: En otras palabras, dos puntos frente a las opciones le permite manejar los errores en su código. ¿La variable contendrá
?
en el caso de una opción no compatible,: en el caso de un valor perdido.
-
OPTARG
- se establece en el valor del argumento actual,-
OPTERR
: indica si Bash debe mostrar mensajes de error.
Entonces el código puede ser:
#!/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
Ejemplo de uso:
$ ./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
Ver: tutorial de pequeños getopts en Bash Hackers Wiki
Sé que esto ya está respondido, pero para el registro y para cualquier persona con los mismos requisitos que yo, decidí publicar esta respuesta relacionada. El código está inundado de comentarios para explicar el código.
Respuesta actualizada:
Guarde el archivo como 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
}
Entonces puedes usarlo así:
#!/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}
Respuesta antigua:
Hace poco necesité usar un enfoque genérico. Me encontré con esta solución:
#!/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}
No probé esto aproximadamente, así que podría tener algunos errores allí.
Usa getopt
¿Por qué getopt?
Para analizar argumentos de línea de comandos elaborados para evitar confusiones y aclarar las opciones, estamos analizando para que el lector de los comandos pueda entender lo que está sucediendo.
¿Qué es getopt?
getopt
se utiliza para dividir (analizar) las opciones en las líneas de comando para facilitar el análisis mediante procedimientos de shell y para verificar opciones legales. Utiliza las rutinas getopt(3)
GNU para hacer esto.
getopt
puede tener los siguientes tipos de opciones.
- Opciones sin valor
- opciones de par clave-valor
Nota: En este documento, durante la explicación de la sintaxis:
- Cualquier cosa dentro de [] es un parámetro opcional en la sintaxis / ejemplos.
- es un marcador de posición, lo que significa que debe sustituirse por un valor real.
¿CÓMO UTILIZAR getopt
?
Sintaxis: Primera Forma
getopt optstring parameters
Ejemplos:
# 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"
Aquí h, v, t son las opciones y -h -v -t es cómo deben darse las opciones en la línea de comandos.
- ''h'' es una opción sin valor.
- ''v:'' implica que la opción -v tiene valor y es una opción obligatoria. '':'' significa que tiene un valor.
- ''t ::'' implica que la opción -t tiene valor pero es opcional. ''::'' significa opcional.
En param opcional, el valor no puede tener una separación de espacios en blanco con la opción. Por lo tanto, en el ejemplo "-t123", -t es la opción 123 es valor.
Sintaxis: Segunda Forma
getopt [getopt_options] [--] [optstring] [parameters]
Aquí después de getopt se divide en cinco partes
- El comando en sí es decir getopt
- El getopt_options, describe cómo analizar los argumentos. Opciones de un solo guión largo, opciones de doble guión.
- -, separa las opciones getopt_options de las opciones que desea analizar y las opciones cortas permitidas
- Las opciones cortas, se toma inmediatamente después - se encuentra. Al igual que la primera sintaxis del formulario.
- Los parámetros, estas son las opciones que has pasado al programa. Las opciones que desea analizar y obtener los valores reales establecidos en ellos.
Ejemplos
getopt -l "name:,version::,verbose" -- "n:v::V" "--name=Karthik -version=5.2 -verbose"
Sintaxis: Tercera Forma
getopt [getopt_options] [-o options] [--] [optstring] [parameters]
Aquí después de getopt se divide en cinco partes
- El comando en sí es decir getopt
- El getopt_options, describe cómo analizar los argumentos. Opciones de un solo guión largo, opciones de doble guión.
- Las opciones cortas es decir -o o --opciones. Al igual que la primera sintaxis del formulario, pero con la opción "-o" y antes de la "-" (doble guión).
- -, separa las opciones getopt_options de las opciones que desea analizar y las opciones cortas permitidas
- Los parámetros, estas son las opciones que has pasado al programa. Las opciones que desea analizar y obtener los valores reales establecidos en ellos.
Ejemplos
getopt -l "name:,version::,verbose" -a -o "n:v::V" -- "-name=Karthik -version=5.2 -verbose"
GETOPT_OPTIONS
getopt_options cambia la forma en que se analizan los parámetros de línea de comandos.
A continuación se presentan algunas de las opciones getopt_
Opción: -l o --opciones
El comando getopt significa que debe permitir el reconocimiento de opciones de múltiples caracteres. Las múltiples opciones están separadas por comas.
Por ejemplo, --name=Karthik
es una opción larga que se envía en la línea de comandos. En getopt, el uso de opciones largas es como
getopt "name:,version" "--name=Karthik"
Como se especifica el nombre: la opción debe contener un valor
Opción: -a o - alternativa
El comando getopt significa que la opción larga debe tener un solo guión ''-'' en lugar del guión doble ''-''.
Ejemplo, en lugar de --name=Karthik
puedes usar solo -name=Karthik
getopt "name:,version" "-name=Karthik"
Un ejemplo completo de script con el código:
#!/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
Ejecutando este archivo de script:
# 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
Ejemplo de POSIX 7
También vale la pena consultar el ejemplo del estándar: 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" "$*"
Y luego podemos probarlo:
$ 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
Probado en Ubuntu 17.10, sh
es dash 0.5.8.
#!/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}"
Ejemplo de ejecución:
$ ./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