Un shell es el intérprete de comandos del sistema operativo. Bash es mi shell favorito, pero todos los shell de Linux interpretan los comandos tecleados por el usuario o el sysadmin en una forma que el sistema operativo pueda utilizar. Cuando los resultados son devueltos al programa shell, éste los envía a STDOUT que, por defecto, los muestra en la terminal. Todas las shell con las que estoy familiarizado son también lenguajes de programación.

Características como la finalización del tabulador, la recuperación y edición de la línea de comandos y los accesos directos como los alias contribuyen a su valor como una potente shell. Su modo de edición de línea de comandos por defecto utiliza Emacs, pero una de mis características favoritas de Bash es que puedo cambiarlo al modo Vi para utilizar comandos de edición que ya forman parte de mi memoria muscular.

Sin embargo, si piensas en Bash únicamente como un shell, te pierdes gran parte de su verdadero poder. Mientras investigaba mi curso de autoestudio de Linux de tres volúmenes (en el que se basa esta serie de artículos), aprendí cosas sobre Bash que nunca había conocido en más de 20 años de trabajo con Linux. Algunos de estos nuevos conocimientos están relacionados con su uso como lenguaje de programación. Bash es un poderoso lenguaje de programación, perfectamente diseñado para su uso en la línea de comandos y en scripts de shell.

Esta serie de tres partes explora el uso de Bash como un lenguaje de programación de interfaz de línea de comandos (CLI). Este primer artículo examina algo de programación simple en la línea de comandos con Bash, variables y operadores de control. Los otros artículos exploran los tipos de archivos Bash; los operadores lógicos de cadena, numéricos y diversos que proporcionan una lógica de control del flujo de ejecución; los diferentes tipos de expansiones del shell; y los bucles for, while y until que permiten realizar operaciones repetitivas. También verán algunos comandos que simplifican y apoyan el uso de estas herramientas.

El shell

Un shell es el intérprete de comandos para el sistema operativo. Bash es mi shell favorito, pero cada shell de Linux interpreta los comandos escritos por el usuario o el administrador del sistema en una forma que el sistema operativo puede utilizar. Cuando los resultados son devueltos al programa shell, éste los muestra en la terminal. Todos los shells que conozco son también lenguajes de programación.

Bash significa Bourne Again Shell porque el shell Bash está basado en el antiguo shell Bourne que fue escrito por Steven Bourne en 1977. Hay muchos otros shells disponibles, pero estos son los cuatro que encuentro con más frecuencia:

  • csh: El shell de C para los programadores a los que les gusta la sintaxis del lenguaje C
  • ksh: El shell Korn, escrito por David Korn y popular entre los usuarios de Unix
  • tcsh: Una versión de csh con más características de facilidad de uso
  • zsh: El shell Z, que combina muchas características de otros shells populares

Todos los shells tienen comandos incorporados que complementan o sustituyen a los proporcionados por las utilidades centrales. Abra la página del manual del shell y busque la sección «BUILT-INS» para ver los comandos que proporciona.

Cada shell tiene su propia personalidad y sintaxis. Algunas te funcionarán mejor que otras. He utilizado el shell C, el shell Korn y el shell Z. Todavía me gusta el shell Bash más que cualquiera de ellos. Utiliza el que mejor te funcione, aunque para ello tengas que probar alguno de los otros. Afortunadamente, es bastante fácil cambiar de shell.

Todos estos shells son lenguajes de programación, así como intérpretes de comandos. Aquí hay un rápido recorrido por algunas construcciones y herramientas de programación que son partes integrales de Bash.

Bash como lenguaje de programación

La mayoría de los administradores de sistemas han utilizado Bash para emitir comandos que suelen ser bastante simples y directos. Pero Bash puede ir más allá de introducir comandos simples, y muchos sysadmins crean programas de línea de comandos simples para realizar una serie de tareas. Estos programas son herramientas comunes que pueden ahorrar tiempo y esfuerzo.

Mi objetivo al escribir programas CLI es ahorrar tiempo y esfuerzo (es decir, ser el sysadmin perezoso). Los programas CLI apoyan esto enumerando varios comandos en una secuencia específica que se ejecutan uno tras otro, por lo que no es necesario mirar el progreso de un comando y escribir el siguiente comando cuando el primero termina. Usted puede ir a hacer otras cosas y no tener que monitorear continuamente el progreso de cada comando.

¿Qué es «un programa»?

El Diccionario Libre de Computación en Línea (FOLDOC) define un programa como: «Las instrucciones ejecutadas por un ordenador, en contraposición al dispositivo físico en el que se ejecutan». El WordNet de la Universidad de Princeton define un programa como: «…una secuencia de instrucciones que un ordenador puede interpretar y ejecutar…» Wikipedia también tiene una buena entrada sobre programas de ordenador.

Por lo tanto, un programa puede consistir en una o más instrucciones que realizan una tarea específica y relacionada. Una instrucción de un programa de ordenador también se llama declaración de programa. Para los administradores de sistemas, un programa es usualmente una secuencia de comandos del shell. Todos los shells disponibles para Linux, al menos con los que estoy familiarizado, tienen al menos una forma básica de capacidad de programación, y Bash, el shell por defecto para la mayoría de las distribuciones de Linux, no es una excepción.

Aunque esta serie utiliza Bash (porque es tan omnipresente), si usted utiliza un shell diferente, los conceptos generales de programación serán los mismos, aunque las construcciones y la sintaxis pueden diferir un poco. Algunos shells pueden soportar algunas características que otros no, pero todos proporcionan alguna capacidad de programación. Los programas de la shell pueden almacenarse en un archivo para su uso repetido, o pueden crearse en la línea de comandos según sea necesario.

Programas sencillos de la CLI

Los programas más sencillos de la línea de comandos son una o dos sentencias de programa consecutivas, que pueden estar relacionadas o no, que se introducen en la línea de comandos antes de pulsar la tecla Intro. La segunda sentencia de un programa, si la hay, puede depender de las acciones de la primera, pero no es necesario que lo haga.

También hay un poco de puntuación sintáctica que debe quedar clara. Cuando se introduce un único comando en la línea de comandos, al pulsar la tecla Enter se termina el comando con un punto y coma implícito (;). Cuando se utiliza en un programa de shell CLI introducido como una sola línea en la línea de comandos, el punto y coma debe utilizarse para terminar cada declaración y separarla de la siguiente. La última declaración en un programa de shell CLI puede utilizar un punto y coma explícito o implícito.

Algunas sintaxis básicas

Los siguientes ejemplos aclararán esta sintaxis. Este programa consiste en un solo comando con un terminador explícito:

$ echo "Hello world." ;
Hello world.

Eso puede no parecer un gran programa, pero es el primer programa que encuentro con cada nuevo lenguaje de programación que aprendo. La sintaxis puede ser un poco diferente para cada lenguaje, pero el resultado es el mismo.

Ampliemos un poco este programa trivial pero omnipresente. Tus resultados serán diferentes a los míos porque yo he hecho otros experimentos, mientras que tú puedes tener sólo los directorios y archivos por defecto que se crean en el directorio principal de la cuenta la primera vez que entras en una cuenta a través del escritorio de la interfaz gráfica.

$ echo "My home directory." ; ls ;
My home directory.
chapter25 TestFile1.Linux dmesg2.txt Downloads newfile.txt softlink1 testdir6
chapter26 TestFile1.mac dmesg3.txt file005 Pictures Templates testdir
TestFile1 Desktop dmesg.txt link3 Public testdir Videos
TestFile1.dos dmesg1.txt Documents Music random.txt testdir1

Eso tiene un poco más de sentido. Los resultados están relacionados, pero las declaraciones individuales del programa son independientes entre sí. Fíjate que me gusta poner espacios antes y después del punto y coma porque hace que el código sea un poco más fácil de leer. Pruebe ese pequeño programa CLI de nuevo sin un punto y coma explícito al final:

$ echo "My home directory." ; ls 

No hay diferencia en la salida.

Algo sobre las variables

Como todos los lenguajes de programación, el shell Bash puede tratar con variables. Una variable es un nombre simbólico que se refiere a una ubicación específica en la memoria que contiene un valor de algún tipo. El valor de una variable es cambiable, es decir, es variable.

Bash no tipifica las variables como C y lenguajes relacionados, definiéndolas como enteros, puntos flotantes o tipos de cadena. En Bash, todas las variables son cadenas. Una cadena que es un entero puede ser usada en aritmética de enteros, que es el único tipo de matemáticas que Bash es capaz de hacer. Si se requiere una matemática más compleja, el comando bc puede ser usado en programas y scripts CLI.

Las variables son valores asignados y pueden ser usadas para referirse a esos valores en programas y scripts CLI. El valor de una variable se establece utilizando su nombre, pero no va precedido del signo $. La asignación VAR=10 establece el valor de la variable VAR en 10. Para imprimir el valor de la variable, puede utilizar la sentencia echo $VAR. Comience con las variables de texto (es decir, no numéricas).

Las variables de Bash pasan a formar parte del entorno del shell hasta que se desestablezcan.

Compruebe el valor inicial de una variable que no haya sido asignada; debería ser nulo. Luego asigne un valor a la variable e imprímalo para verificar su valor. Puede hacer todo esto en un solo programa CLI:

$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World
$

Nota: La sintaxis de la asignación de variables es muy estricta. No debe haber espacios a ambos lados del signo igual (=) en la sentencia de asignación.

La línea vacía indica que el valor inicial de MiVar es nulo. El cambio y la fijación del valor de una variable se hacen de la misma manera. Este ejemplo muestra tanto el valor original como el nuevo.

Como se ha mencionado, Bash puede realizar cálculos aritméticos con números enteros, lo cual es útil para calcular una referencia a la ubicación de un elemento en un array o hacer problemas matemáticos simples. No es adecuado para el cálculo científico o cualquier cosa que requiera decimales, como los cálculos financieros. Hay herramientas mucho mejores para ese tipo de cálculos.

Aquí tienes un cálculo sencillo:

$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63

¿Qué ocurre cuando realizas una operación matemática que da como resultado un número en coma flotante?

El resultado es el entero más cercano. Observe que el cálculo se ha realizado como parte de la sentencia echo. La operación matemática se realiza antes que el comando echo que la encierra debido al orden de precedencia de Bash. Para más detalles vea la página man de Bash y busque «precedence».»

Operadores de control

Los operadores de control de Bash son uno de los operadores sintácticos para crear fácilmente algunos programas de línea de comandos interesantes. La forma más sencilla de programa CLI es simplemente encadenar varios comandos en una secuencia en la línea de comandos:

command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;

Todos esos comandos se ejecutan sin problema mientras no se produzcan errores. Pero, ¿qué ocurre cuando se produce un error? Usted puede anticipar y permitir los errores utilizando los operadores de control incorporados && y || de Bash. Estos dos operadores de control proporcionan cierto control de flujo y le permiten alterar la secuencia de ejecución del código. El punto y coma también se considera un operador de control de Bash, al igual que el carácter de nueva línea.

El operador && simplemente dice, «si el comando1 tiene éxito, entonces ejecuta el comando2. Si el comando1 falla por cualquier razón, entonces el comando2 se salta». Esa sintaxis se ve así:

command1 && command2

Ahora, mira algunos comandos que crearán un nuevo directorio y-si tiene éxito-hacerlo el directorio de trabajo actual (PWD). Asegúrese de que su directorio principal (~) es el PWD. Intente esto primero en /root, un directorio al que no tiene acceso:

El error fue emitido por el comando mkdir. No recibió un error indicando que el archivo no pudo ser creado porque la creación del directorio falló. El operador de control && detectó el código de retorno distinto de cero, por lo que se omitió el comando touch. El uso del operador de control && evita que el comando touch se ejecute porque hubo un error en la creación del directorio. Este tipo de control de flujo del programa de línea de comandos puede evitar que los errores se acumulen y hagan un verdadero desastre. Pero es hora de complicarse un poco más.

El operador de control || permite añadir otra sentencia de programa que se ejecuta cuando la sentencia de programa inicial devuelve un código mayor que cero. La sintaxis básica tiene este aspecto:

command1 || command2 

Esta sintaxis dice: «Si el comando1 falla, ejecuta el comando2». Esto implica que si el comando1 tiene éxito, el comando2 se omite. Pruebe esto intentando crear un nuevo directorio:

Esto es exactamente lo que se espera. Debido a que el nuevo directorio no pudo ser creado, el primer comando falló, lo que resultó en la ejecución del segundo comando.

La combinación de estos dos operadores proporciona lo mejor de ambos. La sintaxis del operador de control que utiliza algún control de flujo toma esta forma general cuando se utilizan los operadores de control && y ||:

preceding commands ; command1 && command2 || command3 ; following commands

Esta sintaxis se puede enunciar así: «Si el comando1 sale con un código de retorno de 0, entonces ejecuta el comando2, en caso contrario ejecuta el comando3». Pruébelo:

Ahora intente de nuevo el último comando utilizando su directorio personal en lugar del directorio /root. Tendrás permiso para crear este directorio:

$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
$

La sintaxis del operador de control, como comando1 && comando2, funciona porque cada comando envía un código de retorno (RC) al shell que indica si se completó con éxito o si hubo algún tipo de fallo durante la ejecución. Por convención, un RC de cero (0) indica éxito, y cualquier número positivo indica algún tipo de fallo. Algunas de las herramientas que usan los sysadmins sólo devuelven un uno (1) para indicar una falla, pero muchas usan otros códigos para indicar el tipo de falla que ocurrió.

La variable del shell Bash $? contiene el RC del último comando. Este RC puede ser comprobado muy fácilmente por un script, el siguiente comando en una lista de comandos, o incluso el sysadmin directamente. Comience ejecutando un comando simple y comprobando inmediatamente el RC. El RC siempre será para el último comando que se ejecutó antes de que lo miraras.

El RC, en este caso, es cero, lo que significa que el comando se completó con éxito. Ahora intente el mismo comando en el directorio principal de root, un directorio para el que no tiene permisos:

En este caso, el RC es dos; esto significa que se denegó el permiso para que un usuario no root acceda a un directorio al que el usuario no tiene permitido el acceso. Los operadores de control utilizan estos RCs para permitirle alterar la secuencia de ejecución del programa.

Resumen

Este artículo examinó Bash como un lenguaje de programación y exploró su sintaxis básica así como algunas herramientas básicas. Ha mostrado cómo imprimir datos en STDOUT y cómo utilizar variables y operadores de control. El siguiente artículo de esta serie examina algunos de los muchos operadores lógicos de Bash que controlan el flujo de la ejecución de instrucciones.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.