![Pasar argumentos a un script](https://rvso.com/image/913177/Pasar%20argumentos%20a%20un%20script.png)
Estoy tomando la clase de Linux Essentials y me estaba yendo bien hasta que llegué al capítulo de secuencias de comandos. Simplemente no entiendo estos conceptos. Me pregunto si alguien puede dividir lo siguiente en términos ultra simplistas o señalarme una mejor referencia para aprenderlo. Actualmente estoy usando el plan de estudios de netacad.
Del libro de texto (con cambios menores de formato):
Hay algunas variables especiales además de las que usted establece. Puede pasar argumentos a su script:
#!/bin/bash echo "Hello $1"
Un signo de dólar seguido de un número N corresponde al enésimo argumento pasado al script. Si llama al ejemplo anterior
./test.sh
el resultado será Hola Linux. La$0
variable contiene el nombre del propio script.Después de ejecutar un programa, ya sea binario o script, devuelve un código de salida que es un número entero entre 0 y 255. Puede probar esto a través de la
$?
variable para ver si el comando anterior se completó correctamente.
Entiendo cómo asignar variables y cómo funcionan con $
pero todo el problema con $0
y $1
... simplemente no lo entiendo.
Cualquier ayuda sería muy apreciada.
Respuesta1
La descripción del libro es incorrecta (o al menos le falta algo). Para que ese script imprima "Hola Linux", lo ejecutarías así:
./test.sh Linux
Si lo ejecuta con just ./test.sh
, solo imprimirá "Hola", porque no hubo un primer argumento y $1
no está definido. Por otro lado, supongamos que lo ejecutas así:
./test.sh foo bar baz
luego, dentro del script, $0
sería "./test.sh", $1
sería "foo", $2
sería "bar" y $3
sería "baz".
En cuanto a $?
, considere el siguiente fragmento de guión:
ls nonexistentfile.txt
echo "The exit status of ls was: $?"
echo "The exit status of echo (the first one) was: $?"
Cuando se ejecute, se imprimirá algo como:
ls: nonexistentfile.txt: No such file or directory
The exit status of ls was: 1
The exit status of echo (the first one) was: 0
El ls
comando no puede enumerar nonexistentfile.txt (porque no existe), por lo que imprime un mensaje de error en ese sentido y sale con un estado distinto de cero para indicar que algo salió mal. El primer echo
comando imprime ese estado de salida ( $?
) y, como lo hace correctamente, sale con un estado de cero. Cuando se ejecuta el segundo echo
comando, proviene $?
del primer echo
comando, por lo que imprime "0".
Por cierto, muchos comandos simplemente usan estados de salida de 0 (éxito) o 1 (algún tipo de falla), pero algunos usan diferentes estados de falla para indicar exactamente qué salió mal. He aquí un extracto delcurl
pagina del manual:
EXIT CODES
There are a bunch of different error codes and their corresponding
error messages that may appear during bad conditions. At the time of
this writing, the exit codes are:
1 Unsupported protocol. This build of curl has no support for this
protocol.
2 Failed to initialize.
3 URL malformed. The syntax was not correct.
...
88 FTP chunk callback reported error
89 No connection available, the session will be queued
90 SSL public key does not matched pinned public key
91 Invalid SSL certificate status.
92 Stream error in HTTP/2 framing layer.
... entonces, un script usado curl
podría verificar $?
qué salió mal y responder de manera diferente según el problema.
Respuesta2
$0
es el nombre que utiliza para ejecutar el script. $1
, $2
y así sucesivamente son los parámetros posicionales del script, que contienen los valores de los argumentos de la línea de comandos que pasó cuando ejecutó el script.
ComoGordon Davisson dijo, el autor del libro debe haber querido decir que correr ./test Linux
imprimiría Hello Linux
. Cuando haces eso, ./test
ingresa al parámetro especial 0
y Linux
ingresa al primer parámetro posicional 1
. El script expande ese primer parámetro posicional precediéndolo con un signo de dólar ( $1
), tal como lo hace con las variables. Si en su lugar hubiera ejecutado ./test Hello Linux for Human Beings
, en el script $1
se expandiría a Linux
, $2
a for
, $3
a Human
y $4
a Beings
.
Puedes escribir un script simple para probar esto:
#!/bin/bash
echo "\$0 expands to '$0'."
echo "\$1 expands to '$1'."
echo "\$2 expands to '$2'."
echo "\$3 expands to '$3'."
(Continúe hasta donde quiera. Para parámetros posicionales superiores a 9
, use la ${
}
forma de expansión de parámetros, por ejemplo, expandir 10
escribiendo ${10}
. En scripts que trabajan con muchos parámetros posicionales, el parámetro especial @
se usa a menudo, evitando la repetición, pero puede ignora eso por ahora si quieres.)
Intente guardarlo en un archivo y marcar el archivo como ejecutable, lo que puede hacer ejecutando chmod +x simple
donde simple
se reemplaza con el nombre del archivo, si es diferente. Luego puedes ejecutarlo usando comandos como ./simple
, ./simple foo
, ./simple foo bar
etc.
Notarás que cuando se pasan menos de tres argumentos de la línea de comandos, los parámetros posicionales que corresponden a los que no se pasaron se expanden a la cadena vacía. Eso es lo que sucede cuando intentas expandir un parámetro de shell que no está definido. Además, notarás que cuando se pasan más argumentos de la línea de comandos, los que pasan del tercero no se utilizan. Probablemente eso es lo que cabría esperar, ya que el guión no hace referencia a ellos en absoluto.
Ahora intenta correr ./simple *
. El shell se expande *
a todos los nombres de archivos en el directorio actual excepto aquellos que comienzan con .
, por lo que tres de ellos se mostrarán como los primeros tres parámetros posicionales (o menos si no hay tantos). Puedes intentar ejecutarlo con otras expansiones de shell, como ./simple {5..10}
.
Puede pasar argumentos de línea de comandos que contengan espacios en blanco encerrándolos entre comillas. Por ejemplo, prueba ./simple 'foo bar' baz
. Observe que esto $1
se expande hasta foo bar
este momento, y no solo hasta foo
.
Porque el caparazón funcionavarias expansiones, no siempre es obvio cuántos argumentos de línea de comando estás pasando a un comando. Una manera fácil de ver cuál será cada argumento es reemplazar el comando con printf '[%s]\n'
. Por ejemplo:
$ printf '[%s]\n' f*
[fonts]
[fstab]
[fuse.conf]
[fwupd]
$ printf '[%s]\n' {1,3}{a..c}
[1a]
[1b]
[1c]
[3a]
[3b]
[3c]
Dado que acaba de iniciar el scripting de shell, elmanual de referencia de bashPuede ser un desafío y es posible que no quieras leerlo de principio a fin. Pero creo que es un recurso valioso incluso si te consideras un completo principiante. Puede encontrar la sección sobreparámetros del shellútil, ya que comienza con lo que ya sabes (variables de shell) y pasa a parámetros especiales como ?
(que la gente suele llamar $?
parámetro, ya que así es como se expande). Para un aprendizaje general sobre Bash, especialmente en un nivel más introductorio, recomiendoestas paginas, incluidoGuía Bash.
Respuesta3
Un buen libro que deberías conocer es "The Linux Command Line" de William Shotts, publicado por No Starch Press y disponible en formato PDF gratuito ensitio web del autor.
En cada script de shell, hay una colección de variables numeradas, a las que generalmente se hace referencia como $1
, $2
etc. Estos son los "parámetros posicionales", más comúnmente conocidos como argumentos de línea de comando. Puede pensar en ellas como variables denominadas 1
, 2
, etc. y para obtener sus valores, usaría $1
, $2
, etc. Cuando llama a un script denominado my_script
a través de la línea de comando ./my_script a b c
, obtendrá tres argumentos que se almacenan en las tres variables $1
, $2
, y $3
. No puede asignar estas variables (excepto como grupo), pero puede examinar y utilizar sus valores. Por ejemplo, echo "$1"
imprimiría el primer argumento de su script.
$0
es un poco inusual; es el nombre con el que se llamó el script que estás ejecutando. En el caso anterior tendría el valor ./my_script
. Nuevamente, puede ver su valor pero no cambiarlo.
$?
es el "estado de salida" del comando que acaba de ejecutarse. Si el comando tuvo éxito, entonces su estado de salida será 0
; de lo contrario, será un pequeño entero positivo. Puede comparar $?
con cero para ver si el comando anterior tuvo éxito o falló. Por ejemplo, las siguientes dos líneas de comando ejecutarán el grep
comando y luego harán eco <1>
porque grep
falló y salieron con un estado de 1
(lo que indica que falló).
grep blarg /etc/passwd
echo "<$?>"
El estado de salida es útil para escribir scripts simples como:
#!/bin/bash
# grep_pw: grep for a pattern in /etc/passwd
grep "$1" /etc/passwd
if [[ $? = 0 ]] ;then
echo "Found it"
exit 0
else
echo "Unable to find the pattern '$1' in /etc/passwd"
exit 1
fi
Coloque ese texto en un archivo llamado grep_pw
, cámbielo para que sea ejecutable con chmod 700 grep_pw
, y llámelo como ./grep_pw nologin
para encontrar las líneas /etc/passwd
que contienen la cadena nologin
.
Cuando conocí el shell por primera vez, el siguiente script me pareció invaluable para comprender cómo el shell analiza las líneas de comando y, en consecuencia, qué argumentos de la línea de comando se pasarían a un script.
#!/bin/bash
# echoargs: print all the arguments
counter=1
for a in "$@" ;do
echo "arg $counter=<$a>"
let counter=counter+1
done
Coloque esos contenidos en un archivo llamado echoargs
, cámbielo para que sea ejecutable chmod 700 echoargs
y llámelo como: ./echoargs a "b c" d
.