traduccion - bash script
¿Cómo puedo usar opciones largas con el Bash getopts incorporado? (7)
Aunque esta pregunta se publicó hace más de 2 años, me encontré necesitando soporte para opciones largas al estilo XFree86; Y también quería tomar lo que pudiera de getopts. Considere el interruptor GCC - -rdynamic
. Marque r
como la letra de la bandera, y espero dynamic
dentro de $OPTARG
... pero, quiero rechazar -r dynamic
, al mismo tiempo que acepto otras opciones después de r
.
La idea que he puesto a continuación se basa en la observación de que $OPTIND
será uno más grande que de otra manera si el espacio (un espacio) sigue la bandera. Entonces, defino una variable bash para mantener el valor anterior de $OPTIND
, llamado $PREVOPTIND
, y lo actualizo al final del ciclo while. Si $OPTIND
es 1
mayor que $PREVOPTIND
, no tenemos espacio (es decir, -rdynamic
); y $GAP
se establece en false
. Si, en cambio, $OPTIND
es 2
mayor que $PREVOPTIND
, tenemos un espacio (por ejemplo, -r dynamic
), y $GAP
se establece en true
.
usage() { echo usage: error from $1; exit -1; }
OPTIND=1
PREVOPTIND=$OPTIND
while getopts "t:s:o:" option; do
GAP=$((OPTIND-(PREVOPTIND+1)))
case $option in
t) case "${OPTARG}" in
emp) # i.e. -temp
((GAP)) && usage "-${option} and ${OPTARG}"
TMPDIR="$OPTARG"
;;
*)
true
;;
esac
;;
s) case "${OPTARG}" in
hots) # i.e. -shots
((GAP)) && usage
NUMSHOTS="$OPTARG"
;;
*) usage "-${option} and ${OPTARG}" ;;
esac
;;
o) OUTFILE="$OPTARG" ;;
*) usage "-${option} and ${OPTARG}" ;;
esac
PREVOPTIND=$OPTIND
done
shift $(($OPTIND - 1))
Estoy tratando de analizar una opción -temp
con Bash getopts. Estoy llamando a mi guión así:
./myscript -temp /foo/bar/someFile
Aquí está el código que estoy usando para analizar las opciones.
while getopts "temp:shots:o:" option; do
case $option in
temp) TMPDIR="$OPTARG" ;;
shots) NUMSHOTS="$OPTARG" ;;
o) OUTFILE="$OPTARG" ;;
*) usage ;;
esac
done
shift $(($OPTIND - 1))
[ $# -lt 1 ] && usage
Como explicaron otras personas, Getopts no analiza opciones largas. Puedes usar getopt, pero no es portátil (y está roto en alguna plataforma ...)
Como solución alternativa, puede implementar un bucle de shell. Aquí un ejemplo que transforma las opciones largas en opciones cortas antes de usar el comando estándar getopts (es más simple en mi opinión):
# Transform long options to short ones
for arg in "$@"; do
shift
case "$arg" in
"--help") set -- "$@" "-h" ;;
"--rest") set -- "$@" "-r" ;;
"--ws") set -- "$@" "-w" ;;
*) set -- "$@" "$arg"
esac
done
# Default behavior
rest=false; ws=false
# Parse short options
OPTIND=1
while getopts "hrw" opt
do
case "$opt" in
"h") print_usage; exit 0 ;;
"r") rest=true ;;
"w") ws=true ;;
"?") print_usage >&2; exit 1 ;;
esac
done
shift $(expr $OPTIND - 1) # remove options from positional parameters
Es cierto que las getopts
bash getopts
solo analizan opciones cortas, pero aún puede agregar algunas líneas de scripting para que getopts maneje opciones largas.
Aquí hay una parte del código que se encuentra en http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts
#== set options ==#
SCRIPT_OPTS='':fbF:B:-:h''
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
[foo]=f
[bar]=b
[foobar]=F
[barfoo]=B
[help]=h
[man]=h
)
#== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
#== translate long options to short ==#
if [[ "x$OPTION" == "x-" ]]; then
LONG_OPTION=$OPTARG
LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d''='' -f2)
LONG_OPTIND=-1
[[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d''='' -f1)
[[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="/$$LONG_OPTIND"
OPTION=${ARRAY_OPTS[$LONG_OPTION]}
[[ "x$OPTION" = "x" ]] && OPTION="?" OPTARG="-$LONG_OPTION"
if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then
OPTION=":" OPTARG="-$LONG_OPTION"
else
OPTARG="$LONG_OPTARG";
if [[ $LONG_OPTIND -ne -1 ]]; then
[[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
shift $OPTIND
OPTIND=1
fi
fi
fi
fi
#== options follow by another option instead of argument ==#
if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then
OPTARG="$OPTION" OPTION=":"
fi
#== manage options ==#
case "$OPTION" in
f ) foo=1 bar=0 ;;
b ) foo=0 bar=1 ;;
B ) barfoo=${OPTARG} ;;
F ) foobar=1 && foobar_name=${OPTARG} ;;
h ) usagefull && exit 0 ;;
: ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
esac
done
shift $((${OPTIND} - 1))
Aquí hay una prueba:
# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2
# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2
De lo contrario, en el reciente Korn Shell ksh93, getopts
puede analizar naturalmente opciones largas e incluso mostrar una página de manual por igual. (vea http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options )
Michel VONGVILAY.
Gracias a @mcoolive.
Pude usar su idea $ @ para convertir opciones de palabra completa y larga en opciones de una sola letra. Quería notarle a cualquiera que utilizara esta idea que también tenía que incluir shift $(expr $OPTIND - 1)
antes de ejecutar los argumentos a través del bucle getopts.
Propósito totalmente diferente pero esto está funcionando bien.
# convert long word options to short word for ease of use and portability
for argu in "$@"; do
shift
#echo "curr arg = $1"
case "$argu" in
"-start"|"--start")
# param=param because no arg is required
set -- "$@" "-s"
;;
"-pb"|"--pb"|"-personalbrokers"|"--personalbrokers")
# pb +arg required
set -- "$@" "-p $1"; #echo "arg=$argu"
;;
"-stop"|"--stop")
# param=param because no arg is required
set -- "$@" "-S"
;;
# the catch all option here removes all - symbols from an
# argument. if an option is attempted to be passed that is
# invalid, getopts knows what to do...
*) [[ $(echo $argu | grep -E "^-") ]] && set -- "$@" "${argu//-/}" || echo "no - symbol. not touching $argu" &>/dev/null
;;
esac
done
#echo -e "/n final option conversions = $@/n"
# remove options from positional parameters for getopts parsing
shift $(expr $OPTIND - 1)
declare -i runscript=0
# only p requires an argument hence the p:
while getopts "sSp:" param; do
[[ "$param" == "p" ]] && [[ $(echo $OPTARG | grep -E "^-") ]] && funcUsage "order"
#echo $param
#echo "OPTIND=$OPTIND"
case $param in
s)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it''s arg $OPTARG"
arg0=start
;;
p)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it''s arg $OPTARG"
[[ "$OPTARG" == "all" ]] && echo -e "argument /"$OPTARG/" accepted. continuing." && (( runscript += 1 )) || usage="open"
[[ $( echo $pbString | grep -w "$OPTARG" ) ]] && echo -e "pb $OPTARG was validated. continuing./n" && (( runscript += 1 )) || usage="personal"
[[ "$runscript" -lt "1" ]] && funcUsage "$usage" "$OPTARG"
arg0=start
;;
S)
OPTARG=${OPTARG/ /}
getoptsRan=1
echo "$param was passed and this is it''s arg $OPTARG"
arg0=stop
;;
*)
getoptsRan=1
funcUsage
echo -e "Invalid argument/n"
;;
esac
done
funcBuildExcludes "$@"
shift $((OPTIND-1))
No puede usar el Getopts Bash builtin para opciones largas, al menos no sin tener que crear sus propias funciones de análisis. Debería echar un vistazo al binario / usr / bin / getopt (proporcionado en mi sistema por el paquete util-linux ; su millaje puede variar).
Vea getopt (1) para opciones de invocación específicas.
getopts
es usado por los procedimientos de shell para analizar parámetros posicionales de 1 carácter solamente (no hay opciones largas de estilo GNU (--myoption) o opciones largas de estilo XF86 (-myoption))
getopts
solo puede analizar opciones cortas.
La mayoría de los sistemas también tienen un comando externo getopt
, pero getopt no es estándar, y generalmente está roto por diseño ya que no puede manejar todos los argumentos de forma segura (argumentos con espacios en blanco y vacíos), solo GNU getopt puede manejarlos de manera segura, pero solo si Lo usas de una manera específica de GNU.
La opción más fácil es usar ninguno de los dos, simplemente iterar los argumentos del script con un ciclo while y hacer el análisis usted mismo.
Vea http://mywiki.wooledge.org/BashFAQ/035 para un ejemplo.