¿Cómo puedo analizar los argumentos de la línea de comandos en Bash?
Digamos que tengo un script que se llama con esta línea:
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
O este:
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile
¿Cuál es la forma aceptada de analizar esto de tal manera que en cada caso (o alguna combinación de los dos) $v
, $f
, y $d
se pondrá todo a true
y $outFile
será igual a /fizz/someOtherFile
?
29 answers
Método #1: Usando bash sin getopt[s]
Dos formas comunes de pasar argumentos clave-valor-par son:
Bash Separados por espacio (por ejemplo, --option argument
) (sin getopt[s])
Uso ./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 Es igual a Separado (por ejemplo, --option=argument
) (sin getopt[s])
Uso ./myscript.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts
#!/bin/bash
for i in "$@"
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
Para comprender mejor ${i#*=}
busque "Eliminación de subcadenas" en esta guía. Es funcionalmente equivalente a `sed 's/[^=]*=//' <<< "$i"`
que llama a un subproceso innecesario o `echo "$i" | sed 's/[^=]*=//'`
que llama a dos subprocesos innecesarios.
Método # 2: Usando bash con getopt[s]
De: http://mywiki.wooledge.org/BashFAQ/035#getopts
Limitaciones de Getopt (1) (versiones anteriores, relativamente recientes getopt
):
- no puede manejar argumentos que son cadenas vacías
- no se pueden manejar argumentos con espacios en blanco incrustados
Las versiones más recientes getopt
no tienen estos limitacion.
Además, el shell POSIX (y otros) ofrecen getopts
que no tiene estas limitaciones. Aquí hay un ejemplo simplista 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: $@"
# End of file
Las ventajas de getopts
son:
- Es más portátil, y funcionará en otros shells como
dash
. - Puede manejar múltiples opciones individuales como
-vf filename
de la manera típica de Unix, automáticamente.
La desventaja de getopts
es que solo puede manejar opciones cortas (-h
, no --help
) sin código adicional.
Hay un tutorial getopts que explica lo que significan toda la sintaxis y las variables. En bash, también hay help getopts
, que podría ser informativo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-18 07:27:42
Ninguna respuesta menciona getopt mejorado. Y la respuesta mejor votada es engañosa: Ignora las opciones cortas de estilo -vfd
(solicitadas por el OP), las opciones después de los argumentos posicionales (también solicitados por el OP) e ignora los errores de análisis. Lugar:
-
Use mejorado
getopt
desde util-linux o anteriormente GNU glibc.1 - Funciona con
getopt_long()
la función C de GNU glibc. - Tiene todo útil características distintivas (los otros no las tienen):
- maneja espacios, caracteres entre comillas e incluso binarios en argumentos2
- puede manejar opciones al final:
script.sh -o outFile file1 file2 -v
- permite
=
-opciones largas de estilo:script.sh --outfile=fileOut --infile fileIn
- Ya es tan viejo3 que a ningún sistema GNU le falta esto (por ejemplo, cualquier Linux lo tiene).
- Puede probar su existencia con:
getopt --test
→ devuelve el valor 4. - Los demás
getopt
o shell-builtingetopts
son de uso limitado.
Las siguientes llamadas
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
Todos devuelven
verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile
Con lo siguiente myscript
#!/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 -- "$@" to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
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 getopt mejorado está disponible en la mayoría de los "bash-systems", incluyendo Cygwin; en OS X try brew install gnu-getopt o sudo port install getopt
2 las convenciones POSIX exec()
no tienen una forma confiable de pasar NULL binario en argumentos de línea de comandos; esos bytes terminan prematuramente el argumento
3 primera versión lanzada en 1997 o antes (solo la rastreé hasta 1997)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-09 18:10:30
De : digitalpeer.com con pequeñas modificaciones
Uso myscript.sh -p=my_prefix -s=dirname -l=libname
#!/bin/bash
for i in "$@"
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}
Para comprender mejor ${i#*=}
busque "Eliminación de subcadenas" en esta guía. Es funcionalmente equivalente a `sed 's/[^=]*=//' <<< "$i"`
que llama a un subproceso innecesario o `echo "$i" | sed 's/[^=]*=//'`
que llama a dos subprocesos innecesarios.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-20 18:35:46
getopt()
/getopts()
es una buena opción. Robado de aquí:
El simple uso de "getopt" se muestra en este mini-script:
#!/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
Lo que hemos dicho es que cualquiera de, -b, - c o-d serán permitidos, pero ese-c es seguido por un argumento (la" c: "dice eso).
Si llamamos a esto " g " y lo probamos:
bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--
Empezamos con dos argumentos, y "getopt" rompe las opciones y pone a cada uno en su propio argumento. Se también añadir "--".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-10 17:03:38
A riesgo de agregar otro ejemplo para ignorar, aquí está mi esquema.
- maneja
-n arg
y--name=arg
- permite argumentos al final
- muestra errores cuerdos si algo está mal escrito
- compatible, no usa bashisms
- legible, no requiere mantener el estado en un bucle
Espero que sea útil para alguien.
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-21 15:05:05
De manera más sucinta
Script.sh
#!/bin/bash
while [[ "$#" > 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"
Uso:
./script.sh -d dev -u
# OR:
./script.sh --deploy dev --uglify
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-07 21:13:24
Llego unos 4 años tarde a esta pregunta, pero quiero devolver. Utilicé las respuestas anteriores como punto de partida para ordenar mi antiguo análisis de adhoc param. Entonces me refactorizado el siguiente código de la plantilla. Maneja tanto parámetros largos como cortos, usando argumentos = o separados por espacio, así como múltiples parámetros cortos agrupados. Finalmente vuelve a insertar cualquier argumento no param en el $1,$2.. variable. Espero que sea útil.
#!/usr/bin/env bash
# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-01 01:20:22
Mi respuesta se basa en gran medida en la respuesta de Bruno Bronosky, pero en cierto modo trituré sus dos implementaciones pure bash en una que uso con bastante frecuencia.
# 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
Esto le permite tener opciones/valores separados por espacios, así como valores definidos iguales.
Para que pueda ejecutar su script usando:
./myscript --foo -b -o /fizz/file.txt
Así Como:
./myscript -f --bar -o=/fizz/file.txt
Y ambos deben tener el mismo resultado final.
VENTAJAS:
Permite tanto-arg=valor como-arg valor
-
Funciona con cualquier nombre arg que puedas usar en bash
- Significa - a o-arg o ar arg o-a-r-g o lo que sea
-
Puro bash. No es necesario aprender / usar getopt o getopts
CONTRAS:
-
No se puede combinar args
- Significa no-abc. Usted debe hacer-a-b-c
Estos son los únicos pros/contras que puedo pensar en la parte superior de mi cabeza
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:33
He encontrado el asunto de escribir análisis portable en scripts tan frustrante que he escrito Argbash - un generador de código FOSS que puede generar los argumentos-análisis de código para su script además de que tiene algunas características agradables:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-09 10:39:37
Creo que este es bastante simple de usar:
#!/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
Ejemplo de invocación:
./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v
OPT:d
OPT:o OPTARG:/fizz/someOtherFile
OPT:f
ARG:./foo/bar/someFile
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-01 15:15:03
Ampliando la excelente respuesta de @ guneysus, aquí hay un ajuste que permite al usuario usar la sintaxis que prefiera, por ejemplo
command -x=myfilename.ext --another_switch
Vs
command -x myfilename.ext --another_switch
Es decir, los iguales se pueden reemplazar con espacios en blanco.
Esta "interpretación difusa" puede no ser de su agrado, pero si está haciendo scripts que son intercambiables con otras utilidades (como es el caso de la mía, que debe funcionar con ffmpeg), la flexibilidad es útil.
STD_IN=0
prefix=""
key=""
value=""
for keyValue in "$@"
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-09 14:07:58
Getopts funciona muy bien si #1 lo tiene instalado y #2 tiene la intención de ejecutarlo en la misma plataforma. OSX y Linux (por ejemplo) se comportan de manera diferente a este respecto.
Aquí hay una solución (no getopts) que admite banderas iguales, no iguales y booleanas. Por ejemplo, podría ejecutar su script de esta manera:
./script --arg1=value1 --arg2 value2 --shouldClean
# parse the arguments.
COUNTER=0
ARGS=("$@")
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-12 22:17:48
Les doy La Función parse_params
que analizará los parámetros desde la línea de comandos.
- Es una solución Bash pura, sin utilidades adicionales.
- No contamina el alcance global.
- Sin esfuerzo te devuelve variables fáciles de usar, sobre las que podrías construir más lógica.
- La cantidad de guiones antes de los parámetros no importa (
--all
es igual a-all
es igual aall=all
)
El script de abajo es una demostración de trabajo copy-paste. Ver show_use
función para entender cómo usar parse_params
.
Limitaciones:
- No admite parámetros delimitados por espacios(
-d 1
) - Los nombres de parámetros perderán guiones por lo que
--any-param
y-anyparam
son equivalentes -
eval $(parse_params "$@")
debe usarse dentro de la función bash (no funcionará en el ámbito global)
#!/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 "$@")
# --
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-06 13:00:40
Así es como lo hago en una función para evitar romper la ejecución de getopts al mismo tiempo en algún lugar más alto en la pila:
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
...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-19 07:50:31
EasyOptions no requiere ningún análisis:
## 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-04 16:47:40
Me gustaría ofrecer mi versión de análisis de opciones, que permite lo siguiente:
-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello
También permite esto (podría ser no deseado):
-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder
Usted tiene que decidir antes de usar si = se va a usar en una opción o no. Esto es para mantener el código limpio (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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-24 10:54:23
Tenga en cuenta que getopt(1)
fue un error de vida corta de AT & T.
Getopt fue creado en 1984 pero ya enterrado en 1986 porque no era realmente utilizable.
Una prueba del hecho de que getopt
está muy desactualizado es que la página de manual de getopt(1)
todavía menciona "$*"
en lugar de "$@"
, que se agregó al Shell de Bourne en 1986 junto con el shell de getopts(1)
para tratar con argumentos con espacios dentro.
Por cierto: si está interesado en analizar opciones largas en shell scripts, puede ser de interés saber que la implementación getopt(3)
de libc (Solaris) y ksh93
agregaron una implementación de opción larga uniforme que admite opciones largas como alias para opciones cortas. Esto hace que ksh93
y Bourne Shell
implementen una interfaz uniforme para opciones largas a través de getopts
.
Un ejemplo para opciones largas tomadas de la página man de Bourne Shell:
getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"
Muestra cuánto tiempo se pueden usar los alias de las opciones tanto en Bourne Shell como en Bourne Shell. ksh93.
Vea la página de manual de un Shell Bourne reciente:
Http://schillix.sourceforge.net/man/man1/bosh.1.html
Y la página de manual para getopt (3) de OpenSolaris:
Http://schillix.sourceforge.net/man/man3c/getopt.3c.html
Y por último, la página de manual de getopt(1) para verificar el outdated*:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-20 21:59:55
Mezcla de argumentos posicionales y basados en flag
Par param = arg (es igual a delimitado)
Mezcla libremente banderas entre argumentos posicionales:
./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d
Se puede lograr con un enfoque bastante conciso:
# 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
Par param arg (espacio delimitado)
Es usualmente más claro no mezclar estilos --flag=value
y --flag value
.
./script.sh dumbo 127.0.0.1 --environment production -q -d
Esto es un poco arriesgado de leer, pero sigue siendo válido
./script.sh dumbo --environment production 127.0.0.1 --quiet -d
Fuente
# 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-27 02:42:47
Supongamos que creamos un script de shell llamado test_args.sh
como sigue
#!/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"
Después de ejecutar el siguiente comando:
sh test_args.sh -year 2017 -flag -month 12 -day 22
La salida sería:
year=2017 month=12 day=22 flag=true
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-10 22:49:30
Use los "argumentos" del módulo de bash-modules
Ejemplo:
#!/bin/bash
. import.sh log arguments
NAME="world"
parse_arguments "-n|--name)NAME;S" -- "$@" || {
error "Cannot parse command line."
exit 1
}
info "Hello, $NAME!"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-09 16:51:09
Esto también puede ser útil saber, puede establecer un valor y si alguien proporciona entrada, sobreescriba el valor predeterminado con ese valor..
Myscript.sh -f ./ serverlist.txt o simplemente ./myscript.sh (y toma valores predeterminados)
#!/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}"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-14 18:01:08
Otra solución sin getopt[s], POSIX, antiguo estilo Unix
Similar a la solución Bruno Bronosky publicó esta aquí es una sin el uso de getopt(s)
.
La principal característica diferenciadora de mi solución es que permite tener opciones concatenadas juntas al igual que tar -xzf foo.tar.gz
es igual a tar -x -z -f foo.tar.gz
. Y al igual que en tar
, ps
etc. el guion inicial es opcional para un bloque de opciones cortas (pero esto se puede cambiar fácilmente). Las opciones largas también son compatibles (pero cuando un bloque comienza con uno, entonces se requieren dos guiones iniciales).
Código con opciones de ejemplo
#!/bin/sh
echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[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
Para el uso de ejemplo por favor vea los ejemplos más abajo.
Posición de las opciones con argumentos
Por lo que vale, las opciones con argumentos no son las últimas (solo las opciones largas tienen que serlo). Por lo tanto, mientras que por ejemplo en tar
(al menos en algunas implementaciones) las opciones f
deben ser las últimas porque el nombre del archivo sigue (tar xzf bar.tar.gz
funciona pero tar xfz bar.tar.gz
no) este no es el caso aquí (ver los ejemplos posteriores).
Múltiples opciones con argumentos
Como otra ventaja, los parámetros de la opción se consumen en el orden de las opciones por los parámetros con opciones requeridas. Basta con mirar la salida de mi script aquí con la línea de comandos abc X Y Z
(o -abc X Y Z
):
Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!
Opciones largas concatenadas también
También puede tener opciones largas en el bloque de opciones dado que se producen en último lugar en el bloque. Así que el las siguientes líneas de comando son todas equivalentes (incluyendo el orden en el que se procesan las opciones y sus argumentos):
-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
Todos estos conducen a:
Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...
No en esta solución
Argumentos opcionales
Las opciones con argumentos opcionales deberían ser posibles con un poco de trabajo, por ejemplo, mirando reenviar si hay un bloque sin guion; el usuario tendría que poner un guion delante de cada bloque después de un bloque con un parámetro que tiene un parámetro opcional. Tal vez esto es demasiado complicado para comunicarse con el usuario, así que mejor solo requiere un guion principal por completo en este caso.
Las cosas se complican aún más con múltiples parámetros posibles. Yo aconsejaría en contra de hacer las opciones tratando de ser inteligente al determinar si el argumento an podría ser para it o no (por ejemplo, con una opción solo toma un número como un argumento opcional) porque esto podría romperse en el futuro.
Personalmente estoy a favor de opciones adicionales en lugar de argumentos opcionales.
Argumentos de opción introducidos con un signo igual
Al igual que con los argumentos opcionales, no soy fan de esto (POR cierto, ¿hay un hilo para discutir los pros/contras de diferentes estilos de parámetros?) pero si quieres esto, probablemente podrías implementarlo tú mismo como lo hiciste en http://mywiki.wooledge.org/BashFAQ/035#Manual_loop con una sentencia de caso --long-with-arg=?*
y luego quitar el signo igual (este es, por cierto, el sitio que dice que hacer concatenación de parámetros es posible con un poco de esfuerzo, pero "lo dejó como un ejercicio para el lector", lo que me hizo tomarlos en su palabra, pero empecé desde cero).
Otras notas
Compatible con POSIX, funciona incluso en configuraciones de Busybox antiguas con las que tuve que lidiar (con p.ej. cut
, head
y getopts
faltar).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:36
Solución que conserva argumentos no manejados. Demos Incluidos.
Aquí está mi solución. Es MUY flexible y a diferencia de otros, no debería requerir paquetes externos y maneja los argumentos sobrantes limpiamente.
El uso es: ./myscript -flag flagvariable -otherflag flagvar2
Todo lo que tienes que hacer es editar la línea validflags. Antepone un guion y busca en todos los argumentos. Luego define el siguiente argumento como el nombre de la bandera, por ejemplo,
./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2
El código principal (versión corta, detallada con ejemplos más abajo, también un versión con errores):
#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
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
La versión detallada con demos de echo integradas:
#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
match=0
argval=$1
# argval=$(echo $@ | 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:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2
echo leftovers: $leftovers
echo rate $rate time $time number $number
El último, este error se produce si se pasa un argumento inválido.
#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
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: Lo que hace, lo maneja muy bien. Conserva argumentos no utilizados que muchas de las otras soluciones aquí no. También permite que las variables sean llamadas sin ser definidas a mano en el script. También permite la prepoblación de variables si no se da ningún argumento correspondiente. (Ver verbose ejemplo).
Contras: No se puede analizar una sola cadena arg compleja e.g.- xcvf procesaría como un solo argumento. Sin embargo, podría escribir código adicional en el mío que agregue esta funcionalidad.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-29 04:07:33
La respuesta principal a esta pregunta parecía un poco complicada cuando la probé here aquí está mi solución que he encontrado que es más robusta:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-01 12:09:07
Este ejemplo muestra cómo usar getopt
y eval
y HEREDOC
y shift
para manejar parámetros cortos y largos con y sin un valor requerido que sigue. También la instrucción switch/case es concisa y fácil de seguir.
#!/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" -- "$@")
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
Las líneas más significativas del script de arriba son estas:
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
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
Corto, directo, legible y maneja casi todo (en mi humilde opinión).
Espero que eso ayude a alguien.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-07 18:25:44
Tengo que escribir un ayudante de bash para escribir una buena herramienta de bash
Inicio del proyecto: https://gitlab.mbedsys.org/mbedsys/bashopts
Ejemplo:
#!/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 "$@"
# Process argument
bashopts_process_args
Dará ayuda:
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:"")
Disfruta :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-20 21:30:19
Aquí está mi enfoque - usando regexp.
- no hay getopts
- maneja bloque de parámetros cortos
-qwerty
- maneja parámetros cortos
-q -w -e
- maneja opciones largas
--qwerty
- puede pasar el atributo a la opción corta o larga (si está utilizando el bloque de opciones cortas, el atributo se adjunta a la última opción)
- puede usar espacios o
=
para proporcionar atributos, pero los atributos coinciden hasta encontrar guion + espacio "delimitador", por lo que en--q=qwe ty
qwe ty
es un atributo - maneja la mezcla de todos los anteriores, por lo que
-o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ribute
es válido
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
}
options=$@
until [ "$options" = "" ]; do
if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
opt=${BASH_REMATCH[3]}
attr=${BASH_REMATCH[7]}
options=${BASH_REMATCH[9]}
elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
pile=${BASH_REMATCH[4]}
while (( ${#pile} > 1 )); do
opt=${pile:0:1}
attr=""
pile=${pile/${pile:0:1}/}
parse_options
done
opt=$pile
attr=${BASH_REMATCH[7]}
options=${BASH_REMATCH[9]}
else # leftovers that don't match
opt=${BASH_REMATCH[10]}
options=""
fi
parse_options
fi
done
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-17 16:48:58
Aquí está mi solución mejorada de la respuesta de Bruno Bronosky usando matrices variables.
Le permite mezclar la posición de los parámetros y darle una matriz de parámetros que preserva el orden sin las opciones
#!/bin/bash
echo $@
PARAMS=()
SOFT=0
SKIP=()
for i in "$@"
do
case $i in
-n=*|--skip=*)
SKIP+=("${i#*=}")
;;
-s|--soft)
SOFT=1
;;
*)
# unknown option
PARAMS+=("$i")
;;
esac
done
echo "SKIP = ${SKIP[@]}"
echo "SOFT = $SOFT"
echo "Parameters:"
echo ${PARAMS[@]}
Mostrará por ejemplo:
$ ./test.sh parameter -s somefile --skip=.c --skip=.obj
parameter -s somefile --skip=.c --skip=.obj
SKIP = .c .obj
SOFT = 1
Parameters:
parameter somefile
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-05 09:17:12
Quiero enviar mi proyecto: https://github.com/flyingangel/argparser
source argparser.sh
parse_args "$@"
Así de simple. El entorno se rellenará con variables con el mismo nombre que los argumentos
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-16 17:45:50