3.3 Lección 1
Certificación: |
Linux Essentials |
---|---|
Versión: |
1.6 |
Tema: |
3 El poder de la línea de comandos |
Objetivo: |
3.3 Convertiendo comandos en un script |
Lección: |
1 de 2 |
Introducción
Hasta ahora hemos aprendido a ejecutar comandos desde la shell, pero también podemos introducir comandos en un archivo, y luego establecer que ese archivo sea ejecutable. Cuando este se ejecuta, los comandos también se ejecutarán uno tras otro. Estos archivos se llama scripts, y son una herramienta absolutamente crucial para cualquier administrador de sistemas Linux.
Imprimiendo salidas (Printing Output)
Comencemos demostrando un comando que puede haber visto en lecciones anteriores: echo
, el cual imprime un argumento en la salida estándar.
$ echo "Hello World!" Hello World!
Ahora, usaremos la redirección de archivos para enviar este comando a un nuevo archivo llamado new_script
.
$ echo 'echo "Hello World!"' > new_script $ cat new_script echo "Hello World!"
El archivo new_script
contiene ahora el mismo comando que antes.
Cómo ejecutar un script
Vamos a demostrar algunos de los pasos necesarios para que este archivo se ejecute de la forma en que esperamos, el primer pensamiento de un usuario podría ser simplemente escribir el nombre del script. La forma en que ellos podrían escribir el nombre de cualquier otro comando:
$ new_script /bin/bash: new_script: command not found
Podemos asumir con seguridad que new_script
existe en nuestra ubicación actual, pero note que el mensaje de error no nos está diciendo que el archivo no existe, sino que nos está diciendo que el comando no existe, sería útil discutir cómo maneja Linux los comandos y los ejecutables.
Comandos y PATH
Cuando escribimos el comando ls
en la shell, por ejemplo, estamos ejecutando un archivo llamado ls
que existe en nuestro sistema de archivos:
$ which ls /bin/ls
Rápidamente se volvería tedioso escribir la ruta absoluta de ls
cada vez que deseemos ver el contenido de un directorio, así que Bash tiene una variable de entorno (environment) que contiene todos los directorios en los que podemos encontrar los comandos que deseamos ejecutar.
$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Cada una de estas ubicaciones es donde el shell espera encontrar un comando, delimitado con dos puntos (:
). Notará que /bin
está presente, pero es seguro asumir que nuestra ubicación actual no lo está. La shell buscará new_script
en cada uno de estos directorios, pero no lo encontrará y por lo tanto lanzará el error que vimos arriba.
Hay tres soluciones para este problema: podemos mover new_script
a uno de los directorios PATH
, podemos añadir nuestro directorio actual a PATH
, o podemos cambiar la forma en que intentamos llamar al script, esta última solución es la más sencilla, simplemente requiere que especifiquemos la ubicación actual cuando llamemos al script usando la barra de puntos (./
).
$ ./new_script /bin/bash: ./new_script: Permission denied
El mensaje de error ha cambiado, lo que indica que hemos hecho algunos progresos.
Ejecutar Permisos
La primera indagación que un usuario debe hacer en este caso es usar ls -l
para mirar el archivo:
$ ls -l new_script -rw-rw-r-- 1 user user 20 Apr 30 12:12 new_script
Podemos ver que los permisos para este archivo están configurados por defecto en 664
, pero todavía no lo hemos configurado este para que tenga permisos de ejecución.
$ chmod +x new_script $ ls -l new_script -rwxrwxr-x 1 user user 20 Apr 30 12:12 new_script
Este comando ha dado permisos de ejecución a todos los usuarios, tenga en cuenta que esto puede ser un riesgo para la seguridad, pero por ahora es un nivel aceptable de permisos.
$ ./new_script Hello World!
Ahora podemos ejecutar nuestro script.
Definición del intérprete
Como hemos demostrado, hemos podido simplemente introducir texto en un archivo, configurarlo como ejecutable y ejecutarlo. El new_script
es funcionalmente un archivo de texto normal, pero logramos que sea interpretado por Bash, pero ¿qué pasa si está escrito en Perl o Python?
Es muy recomendable especificar el tipo de intérprete que queremos usar en la primera línea de un script, este se llama bang line o más conocido como shebang; e indica al sistema cómo queremos que se ejecute este archivo.
Como estamos aprendiendo Bash, estaremos usando la ruta absoluta a nuestro ejecutable Bash, una vez más usando which
:
$ which bash /bin/bash
Nuestro shebang comienza con un signo de hash y un signo de exclamación, seguido de la ruta absoluta de arriba. Ahora abramos new_script
en un editor de texto e insertemos el shebang, y aprovechemos la oportunidad para insertar un comentario en nuestro script, ya que los comentarios son ignorados por el intérprete y están escritos para el beneficio de otros usuarios que deseen entender su script.
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica documentar todos los scripts. echo "Hello World!"
También haremos un cambio adicional al nombre del archivo: guardaremos este archivo como new_script.sh
. El sufijo del archivo .sh
no cambia la ejecución del archivo de ninguna manera, es una convención que los scripts bash sean etiquetados con .sh
o .bash
para identificarlos fácilmente, así como los scripts Python son usualmente identificados con el sufijo .py
.
Editores de texto comunes
Los usuarios de Linux a menudo tienen que trabajar en un entorno en el que los editores gráficos de texto no están disponibles, por lo que es muy recomendable que se familiaricen con la edición de archivos de texto desde la línea de comandos, ya que dos de los editores de texto más comunes son vi
y nano
.
vi
vi
es un venerable editor de texto y está instalado por defecto en casi todos los sistemas Linux existentes. vi
generó un clon llamado vi IMproved o vim
que añade alguna funcionalidad, pero mantiene la interfaz de vi
. Aunque trabajar con vi
es desalentador para un nuevo usuario, el editor es muy popular y querido por los usuarios que aprenden de sus muchas características.
La diferencia más importante entre vi
y aplicaciones como el Bloc de notas es que vi
tiene tres modos diferentes: Al inicio, las teclas H, J, K y L se utilizan para navegar, no para escribir. En modo navegación, puede pulsar I para entrar en modo insertar. En este punto, puede escribir con normalidad. Para salir de modo insertar, pulse Esc para volver a modo navegación.
Aunque vi
tiene una curva de aprendizaje, los diferentes modos pueden permitir con el tiempo que un usuario experto sea más eficiente que con otros editores.
nano
nano
es una herramienta más nueva, construida para ser simple y fácil de usar que vi
. nano
no tiene diferentes modos, sino que un usuario al iniciar puede empezar a escribir, y usa Ctrl para acceder a las herramientas impresas en la parte inferior de la pantalla.
[ Welcome to nano. For basic help, type Ctrl+G. ] ^G Get Help ^O Write Out ^W Where Is ^K Cut Text ^J Justify ^C Cur Pos M-U Undo ^X Exit ^R Read File ^\ Replace ^U Uncut Text ^T To Spell ^_ Go To Line M-E Redo
Los editores de texto son una cuestión de preferencia personal, y el editor que usted elija no tendrá nada que ver con esta lección, pero familiarizarse y sentirse cómodo con uno o más editores de texto valdrá la pena en el futuro.
Variables
Las variables son una parte importante de cualquier lenguaje de programación y Bash no es diferente, cuando se inicia una nueva sesión desde la terminal, la shell ya establece algunas variables, como por ejemplo la variable PATH
, a la que llamamos variables de entorno, ya que suelen definir las características de nuestro entorno de shell, por lo que se pueden modificar y añadir variables de entorno, pero por ahora vamos a centrarnos en la configuración de variables dentro de nuestro script.
Modificaremos nuestro script para que se vea así:
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username=Carol echo "Hello $username!"
En este caso, hemos creado una variable llamada username
y le hemos asignado el valor de Carol
. Tenga en cuenta que no hay espacios entre el nombre de la variable, el signo igual o el valor asignado.
En la siguiente línea, hemos utilizado el comando echo
con la variable, pero hay un signo de dólar ($
) adelante del nombre de la variable, esto es importante, ya que indica a la shell que queremos tratar el nombre de usuario
como una variable y no sólo como una palabra normal. Al introducir $username
en nuestro comando, indicamos que queremos realizar una sustitución, reemplazando el nombre de una variable por el valor asignado a esa variable.
Ejecutando el nuevo script, obtenemos esta salida:
$ ./new_script.sh Hello Carol!
-
Las variables deben contener sólo caracteres alfanuméricos o guiones bajos, y son sensibles a mayúsculas y minúsculas.
Username
yusername
serán tratadas como variables diferentes. -
La sustitución de variables también puede tener el formato
${username}
, con la adición de{ }
. Esto también es aceptable. -
Las variables en Bash tienen un tipo implícito, y son consideradas como cadenas, lo que significa que la ejecución de funciones matemáticas en Bash es más complicada de lo que lo sería en otros lenguajes de programación como C/C++:
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username=Carol x=2 y=4 z=$x+$y echo "Hello $username!" echo "$x + $y" echo "$z"
$ ./new_script.sh Hello Carol! 2 + 4 2+4
Uso de comillas con variables
Hagamos el siguiente cambio en el valor de nuestra variable username
:
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username=Carol Smith echo "Hello $username!"
Ejecutar este script nos dará un error:
$ ./new_script.sh ./new_script.sh: line 5: Smith: command not found Hello !
Ten en cuenta que Bash es un intérprete, y como tal interpreta nuestro script línea por línea, en este caso, interpreta correctamente username=Carol
para estar estableciendo una variable username
con el valor Carol
, pero luego interpreta el espacio indicando el final de esa asignación, y Smith
como el nombre de un comando.
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username="Carol Smith" echo "Hello $username!"
$ ./new_script.sh Hello Carol Smith!
Una cosa importante a tener en cuenta en Bash es que las comillas dobles y las comillas simples ('
) se comportan de forma muy diferente, ya que se consideran “débiles” porque permiten al intérprete realizar la sustitución dentro de las comillas, mientras que las comillas simples se consideran “fuertes” porque evitan que se produzca cualquier sustitución, como en el siguiente ejemplo:
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username="Carol Smith" echo "Hello $username!" echo 'Hello $username!'
$ ./new_script.sh Hello Carol Smith! Hello $username!
En el segundo comando echo
, se le ha impedido al intérprete sustituir $username
por Carol Smith
, por lo que la salida se toma literalmente.
Argumentos
Por ejemplo, rm testfile
contiene tanto el archivo ejecutable rm
como el archivo de prueba de argumento
. Los argumentos se pueden pasar al script una vez ejecutado, y modifican el comportamiento del script, por lo que son fáciles de implementar.
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username=$1 echo "Hello $username!"
En lugar de asignar un valor a username
directamente dentro del script, le estamos asignando el valor de una nueva variable $1
. Esto se refiere al valor del primer argumento.
$ ./new_script.sh Carol Hello Carol!
Hay maneras de manejar más de nueve argumentos, pero eso está fuera del alcance de esta lección:
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username1=$1 username2=$2 echo "Hello $username1 and $username2!"
$ ./new_script.sh Carol Dave Hello Carol and Dave!
Hay una consideración importante cuando se usan argumentos: En el ejemplo anterior, hay dos argumentos Carol
y Dave
, asignados a $1
y $2
respectivamente. Si falta el segundo argumento, por ejemplo, el intérprete de comandos no lanzará un error, el valor de $2
será simplemente null, o nada en absoluto.
$ ./new_script.sh Carol Hello Carol and !
En nuestro caso, sería una buena idea introducir algo de lógica en nuestro script para que las diferentes condiciones afecten a la salida que deseamos imprimir, comenzando por introducir otra variable útil y pasando a crear ifs.
Devolver el número de argumentos
Mientras que variables como $1
y $2
contienen el valor de los argumentos posicionales, otra variable $#
contiene el número de argumentos.
#!/bin/bash # Este es nuestro primer comentario. También es una buena práctica comentar todos los scripts. username=$1 echo "Hello $username!" echo "Number of arguments: $#."
$ ./new_script.sh Carol Dave Hello Carol! Number of arguments: 2.
Lógica condicional
El uso de la lógica condicional en la programación es un tema vasto y no se tratará en profundidad en esta lección. Nos enfocaremos en la sintaxis de los condicionales en Bash, que difiere de la mayoría de los otros lenguajes de programación.
Comencemos repasando lo que esperamos conseguir, tenemos un sencillo script que debería poder imprimir un saludo a un solo usuario, y si hay algo más que un usuario, deberíamos imprimir un mensaje de error.
-
La condición que estamos probando es el número de usuarios, que está contenido en la variable
$#
. Nos gustaría saber si el valor de$#
es1
. -
Si la condición es true, la acción que tomaremos es saludar al usuario.
-
Si la condición es falso, imprimiremos un mensaje de error.
Ahora que la lógica está clara, nos centraremos en la sintaxis necesaria para implementar esta lógica.
#!/bin/bash # A simple script to greet a single user. if [ $# -eq 1 ] then username=$1 echo "Hello $username!" else echo "Please enter only one argument." fi echo "Number of arguments: $#."
La lógica condicional se encuentra entre if
y fi
, la condición para realizar la prueba se encuentra entre corchetes [ ]
, y la acción a tomar en caso de que la condición sea cierta se indica después de then
, tenga en cuenta los espacios entre los corchetes y la lógica contenida.
Este script mostrará nuestro saludo o el mensaje de error, pero siempre imprimirá la línea Number of arguments
.
$ ./new_script.sh Please enter only one argument. Number of arguments: 0. $ ./new_script.sh Carol Hello Carol! Number of arguments: 1.
Tome nota de la declaración if
. Hemos usado -eq
para hacer una comparación numérica. En este caso, estamos probando que el valor de $#
es igual a uno. Las otras comparaciones que podemos realizar son:
-ne
-
No igual a
-gt
-
Mayor que
-ge
-
Mayor que o igual a
-lt
-
Menos que
-le
-
Menor que o igual a
Ejercicios guiados
-
El usuario escribe lo siguiente en su shell:
$ PATH=~/scripts $ ls Command 'ls' is available in '/bin/ls' The command could not be located because '/bin' is not included in the PATH environment variable. ls: command not found
-
¿Qué ha hecho el usuario?
-
¿Qué comando combinará el valor actual de
PATH
con el nuevo directorio~/scripts
?
-
-
Considere el siguiente script. Observe que está usando
elif
para verificar una segunda condición:> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
Las líneas marcadas con un
>
contienen errores. Arregle los errores.
-
-
¿Cuál será la salida en las siguientes situaciones?
$ ./guided1.sh 3 0
$ ./guided1.sh 2 4
$ ./guided1.sh 0 1
Ejercicios exploratorios
-
Escriba un script simple que verifique si se pasan exactamente dos argumentos. Si es así, imprima los argumentos en orden inverso. Considere este ejemplo (nota: su código puede verse diferente a esto, pero debería conducir a la misma salida):
if [ $1 == $number ] then echo "True!" fi
-
Este código es correcto, pero no es una comparación de números. Use una búsqueda en Internet para descubrir cómo este código es diferente de usar
-eq
. -
Hay una variable de entorno que imprimirá el directorio actual. Use
env
para descubrir el nombre de esta variable. -
Usando lo que has aprendido en las preguntas 2 y 3, escribe un guión corto que acepte un argumento. Si se pasa un argumento, verifique si este coincide con el nombre del directorio actual. Si es así, escriba
sí
. De lo contrario, imprimano
.
Resumen
En esta lección usted aprendió:
-
¿Cómo crear y ejecutar scripts simples?
-
¿Cómo usar un shebang para especificar un intérprete?
-
¿Cómo establecer y usar variables dentro de scripts?
-
¿Cómo manejar argumentos en scripts?
-
¿Cómo construir declaraciones
if
? -
¿Cómo comparar números usando operadores numéricos?
Comandos utilizados en los ejercicios:
echo
-
Imprime una cadena a la salida estándar.
env
-
Imprime todas las variables de entorno a la salida estándar.
which
-
Imprime la ruta absoluta de un comando.
chmod
-
Cambia los permisos de un archivo.
Comandos utilizados en los ejercicios:
$1, $2, … $9
-
Contiene argumentos posicionales pasados al script.
$#
-
Contiene el número de argumentos pasados al script.
$PATH
-
Contiene los directorios que tienen archivos ejecutables utilizados por el sistema.
Comandos utilizados en los ejercicios:
-ne
-
No igual a
-gt
-
Mayor que
-ge
-
Mayor que o igual a
-lt
-
Menos que
-le
-
Menor que o igual a
Respuestas a los ejercicios guiados
-
El usuario escribe lo siguiente en su shell:
$ PATH=~/scripts $ ls Command 'ls' is available in '/bin/ls' The command could not be located because '/bin' is not included in the PATH environment variable. ls: command not found
-
¿Qué ha hecho el usuario?
El usuario ha sobrescrito los contenidos de PATH con el directorio
~/scripts
. El comandols
ya no se puede encontrar, ya que no está contenido en PATH. Tenga en cuenta que este cambio solo afecta a la sesión actual, al cerrar sesión y volver a iniciar, el cambio será revertido -
¿Qué comando combinará el valor actual de
PATH
con el nuevo directorio~/scripts
?PATH=$PATH:~/scripts
-
-
Considere el siguiente script. Observe que está usando
elif
para verificar una segunda condición:> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
The lines marked with a
>
contain errors. Fix the errors.#!/bin/bash fruit1=Apples fruit2=Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" elif [ $1 -gt $2 ] then echo "$fruit1 win!" else echo "$fruit2 win!" fi
-
-
¿Cuál será la salida en las siguientes situaciones?
$ ./guided1.sh 3 0
Apples win!
$ ./guided1.sh 2 4
Oranges win!
$ ./guided1.sh 0 1
This is like comparing Apples and Oranges!
Respuestas a los ejercicios exploratorios
-
Escriba un script simple que verifique si se pasan exactamente dos argumentos. Si es así, imprima los argumentos en orden inverso. Considere este ejemplo (nota: su código puede verse diferente a esto, pero debería conducir a la misma salida):
if [ $1 == $number ] then echo "True!" fi
#!/bin/bash if [ $# -ne 2 ] then echo "Error" else echo "$2 $1" fi
-
Este código es correcto, pero no es una comparación de números. Use una búsqueda en Internet para descubrir cómo este código es diferente de usar
-eq
.El uso de
==
comparará strings. Es decir, si los caracteres de ambas variables coinciden exactamente, entonces la condición es verdadera.abc == abc
true
abc == ABC
false
1 == 1
true
1+1 == 2
false
Las comparaciones de cadenas conducen a un comportamiento inesperado si está probando números.
-
Hay una variable de entorno que imprimirá el directorio actual. Use
env
para descubrir el nombre de esta variable.PWD
-
Usando lo que has aprendido en las preguntas 2 y 3, escribe un guión corto que acepte un argumento. Si se pasa un argumento, verifique si este coincide con el nombre del directorio actual. Si es así, escriba
sí
. De lo contrario, imprimano
.#!/bin/bash if [ "$1" == "$PWD" ] then echo "yes" else echo "no" fi