Pasar argumentos a un script

Pasar argumentos a un script

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.shel resultado será Hola Linux. La $0variable 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 $0y $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 $1no está definido. Por otro lado, supongamos que lo ejecutas así:

./test.sh foo bar baz

luego, dentro del script, $0sería "./test.sh", $1sería "foo", $2sería "bar" y $3serí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 lscomando 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 echocomando imprime ese estado de salida ( $?) y, como lo hace correctamente, sale con un estado de cero. Cuando se ejecuta el segundo echocomando, proviene $?del primer echocomando, 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 delcurlpagina 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 curlpodría verificar $?qué salió mal y responder de manera diferente según el problema.

Respuesta2

$0es el nombre que utiliza para ejecutar el script. $1, $2y 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 Linuximprimiría Hello Linux. Cuando haces eso, ./testingresa al parámetro especial 0y Linuxingresa 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 $1se expandiría a Linux, $2a for, $3a Humany $4a 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 10escribiendo ${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 simpledonde simplese reemplaza con el nombre del archivo, si es diferente. Luego puedes ejecutarlo usando comandos como ./simple, ./simple foo, ./simple foo baretc.

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 $1se expande hasta foo bareste 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, $2etc. 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_scripta 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.

$0es 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 grepcomando y luego harán eco <1>porque grepfalló 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 nologinpara encontrar las líneas /etc/passwdque 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 echoargsy llámelo como: ./echoargs a "b c" d.

información relacionada