¿Cómo escribir un script bash que tome argumentos de entrada opcionales?

Respuestas:

707

Puede usar la sintaxis de valor predeterminado:

somecommand ${1:-foo}

Lo anterior, como se describe en el Manual de referencia de Bash - 3.5.3 Expansión del parámetro Shell [énfasis mío]:

Si el parámetro no está establecido o es nulo , la expansión de la palabra se sustituye. De lo contrario, el valor del parámetro se sustituye.

Si solo desea sustituir un valor predeterminado si el parámetro no está establecido (pero no si es nulo, por ejemplo, si es una cadena vacía), utilice esta sintaxis en su lugar:

somecommand ${1-foo}

Nuevamente del Manual de referencia de Bash - 3.5.3 Expansión del parámetro Shell :

Omitir los dos puntos da como resultado una prueba solo para un parámetro que no está establecido. Dicho de otra manera, si se incluyen los dos puntos, el operador comprueba la existencia de ambos parámetros y que su valor no es nulo; si se omiten los dos puntos, el operador solo prueba la existencia.

Itay Perl
fuente
53
Tenga en cuenta la diferencia semántica entre el comando anterior, "devolver foosi no $1está configurado o una cadena vacía " y ${1-foo}"devolver foosi no $1está configurado".
l0b0
12
¿Puedes explicar por qué esto funciona? Especialmente, ¿cuál es la función / propósito de ':' y '-'?
jwien001
8
@ jwein001: en la respuesta presentada anteriormente, se utiliza un operador de sustitución para devolver un valor predeterminado si la variable no está definida. Específicamente, la lógica es "Si $ 1 existe y no es nulo, devuelve su valor; de lo contrario, devuelve foo". El colon es opcional. Si se omite, el cambio "existe y no es nulo" a solo "existe". El signo menos especifica devolver foo sin establecer $ 1 igual a 'foo'. Los operadores de sustitución son una subclase de operadores de expansión. Consulte la sección 6.1.2.1 de Robbins and Beebe's Classic Shell Scripting [O'Reilly] ( shop.oreilly.com/product/9780596005955.do )
Jubbles el
55
@Jubbles o si no quieres comprar un libro completo para una simple referencia ... tldp.org/LDP/abs/html/parameter-substitution.html
Ohad Schneider
2
Esta respuesta sería aún mejor si mostrara cómo hacer que el valor predeterminado sea el resultado de ejecutar un comando, como lo hace @hagen (aunque esa respuesta es poco elegante).
salteado
310

Puede establecer un valor predeterminado para una variable así:

somecommand.sh

#!/usr/bin/env bash

ARG1=${1:-foo}
ARG2=${2:-bar}
ARG3=${3:-1}
ARG4=${4:-$(date)}

echo "$ARG1"
echo "$ARG2"
echo "$ARG3"
echo "$ARG4"

Aquí hay algunos ejemplos de cómo funciona esto:

$ ./somecommand.sh
foo
bar
1
Thu Mar 29 10:03:20 ADT 2018

$ ./somecommand.sh ez
ez
bar
1
Thu Mar 29 10:03:40 ADT 2018

$ ./somecommand.sh able was i
able
was
i
Thu Mar 29 10:03:54 ADT 2018

$ ./somecommand.sh "able was i"
able was i
bar
1
Thu Mar 29 10:04:01 ADT 2018

$ ./somecommand.sh "able was i" super
able was i
super
1
Thu Mar 29 10:04:10 ADT 2018

$ ./somecommand.sh "" "super duper"
foo
super duper
1
Thu Mar 29 10:05:04 ADT 2018

$ ./somecommand.sh "" "super duper" hi you
foo
super duper
hi
you
Brad Parks
fuente
2
Ah ok El -me confundió (¿se niega?).
Raffi Khatchadourian
55
No, esa es solo una forma extraña que bash tiene de hacer la tarea. Agregaré algunos ejemplos más para aclarar esto un poco ... ¡gracias!
Brad Parks el
44
if [ ! -z $1 ] 
then 
    : # $1 was given
else
    : # $1 was not given
fi
Irit Katriel
fuente
1
Técnicamente, si pasa una cadena vacía '', eso podría contar como un parámetro, pero su verificación lo perderá. En ese caso, $ # diría cuántos parámetros se dieron
vmpstr
18
-nes el mismo que ! -z.
l0b0
Consigo diferentes resultados utilizando -ny ! -zasí que diría que no es el caso aquí.
Eliezer
porque no pudo citar la variable, [ -n $1 ] siempre será cierto . Si usa bash, [[ -n $1 ]]se comportará como espera, de lo contrario debe citarlo[ -n "$1" ]
glenn jackman
21

Puede verificar el número de argumentos con $#

#!/bin/bash
if [ $# -ge 1 ]
then
    $1
else
    foo
fi
Dennis
fuente
10

por favor no olvides, si su variable $ 1 .. $ n necesitas escribir en una variable regular para usar la sustitución

#!/bin/bash
NOW=$1
echo  ${NOW:-$(date +"%Y-%m-%d")}
hagen
fuente
2
La respuesta de Brad anterior prueba que las variables de argumento también se pueden sustituir sin variables intermedias.
Vadzim
2
+1 por señalar la forma de usar un comando como fecha como predeterminado en lugar de un valor fijo. Esto también es posible:DAY=${1:-$(date +%F -d "yesterday")}
Garren S
3

Para argumentos múltiples opcionales , por analogía con el lscomando que puede tomar uno o más archivos o, por defecto, enumera todo en el directorio actual:

if [ $# -ge 1 ]
then
    files="$@"
else
    files=*
fi
for f in $files
do
    echo "found $f"
done

No funciona correctamente para archivos con espacios en la ruta, por desgracia. Todavía no he descubierto cómo hacer que eso funcione.

Jesse Glick
fuente
2

Esto permite el valor predeterminado para el 1er argumento opcional, y conserva múltiples argumentos.

 > cat mosh.sh
   set -- ${1:-xyz} ${@:2:$#} ; echo $*    
 > mosh.sh
   xyz
 > mosh.sh  1 2 3
   1 2 3 
mosh
fuente
1

Es posible utilizar la sustitución de variables para sustituir un valor fijo o un comando (como date) para un argumento. Las respuestas hasta ahora se han centrado en valores fijos, pero esto es lo que solía hacer de la fecha un argumento opcional:

~$ sh co.sh
2017-01-05

~$ sh co.sh 2017-01-04
2017-01-04

~$ cat co.sh

DAY=${1:-$(date +%F -d "yesterday")}
echo $DAY
Garren S
fuente