Un shell es un programa que te permite ingresar comandos para interactuar con el sistema operativo. Los shells pueden ser gráficos; el entorno de escritorio que usas con el mouse o el trackpad es un shell. Nos enfocaremos en las interfaces de línea de comandos, donde escribes comandos y los resultados se imprimen (usualmente) en tu terminal.
Existen muchos shells de línea de comandos diferentes. Windows ofrece cmd y powershell. Mac y Linux ofrecen «shells de Unix», de los cuales hay muchos. El shell predeterminado de Mac es zsh. csh es un shell heredado con una sintaxis similar a la de C. bash es uno de los shells más populares, sobre todo en Linux.
Uno de los primeros shells es el shell de Bourne, sh. Con el tiempo, este shell se estandarizó para que estuviera disponible en todos los sistemas tipo Unix. Muchos shells se basan en el shell de Bourne y con frecuencia ofrecen funcionalidad adicional de tipo interactivo o de programación.
Bash se basa en el shell de Bourne. Por eso se llama el «Bourne-again shell». Es el shell predeterminado en muchos sistemas Linux. Bash ofrece muchas facilidades que lo convierten en un shell interactivo agradable, entre ellas:
Este track no trata sobre cómo usar bash como shell interactivo. Trata sobre aprender a escribir programas con bash. El énfasis estará en usar solo bash para resolver problemas, aunque no hay restricciones para llamar a herramientas externas.
Bash lee comandos de su entrada (que suele ser una terminal o un archivo). Cada línea de entrada que lee se trata como un comando, es decir, una instrucción que debe llevarse a cabo. (Hay algunos casos avanzados, como los comandos que abarcan varias líneas, que veremos más adelante.)
Bash divide cada línea en palabras delimitadas por un carácter de espacio en blanco (espacios y tabulaciones). La primera palabra de la línea es el nombre del comando que se va a ejecutar. Todas las palabras restantes se convierten en argumentos de ese comando (opciones, nombres de archivos, etc.).
En los ejemplos a continuación, te mostraremos algunos comandos interactivos que puedes ejecutar.
# inicia un comentario.
Cualquier palabra que siga al comentario la ignora el shell; el comentario es solo para leerlo.$ indica el prompt del shell.
Si copias el comando, no incluyas el prompt.# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
rm es una aplicación que elimina los archivos que se le pasan.
* es un carácter glob, un patrón «comodín» que coincide con todos los archivos del directorio actual.
Hablaremos más sobre los globs más adelante.
¿Notaste que hay varios espacios entre a y b, y solo uno entre b y c?
¡La cantidad de espacio en blanco entre los argumentos no importa!
Por ejemplo:
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
echo es un comando que imprime sus argumentos en la salida estándar (que en nuestro caso es la terminal).
Bash divide el comando en cinco palabras y llama al comando echo con cuatro argumentos.
Los espacios adicionales del segundo ejemplo no marcan ninguna diferencia.
echo no sabe cómo estaban originalmente los espacios entre las palabras.
Si queremos conservar el espacio en blanco adicional, necesitamos pasar la oración como un único argumento. Podemos hacerlo usando comillas:
$ echo "This is a test."
This is a test.
Las comillas agrupan todo lo que hay dentro de ellas en un único argumento.
El argumento es This is a test., incluidos esos caracteres de espacio específicos.
Ten en cuenta que las comillas no forman parte del argumento: bash las elimina antes de entregar ese único argumento a echo.
Los argumentos se separan del nombre del comando y entre sí mediante un espacio en blanco. Es importante recordarlo. Por ejemplo, lo siguiente es incorrecto:
$ [-f file]
bash: [-f: command not found
Esto pretende comprobar la existencia de un archivo llamado file.
Es razonable suponer que el espacio en blanco alrededor de [ y ] no importa, pero [ en realidad es un comando, y requiere que su último argumento sea ].
(Veremos el comando [ con más detalle más adelante.)
Por lo tanto, debemos separar [ de -f y ] de file.
De lo contrario, bash pensará que estamos intentando ejecutar un comando llamado [-f con un único argumento file].
El comando correcto separa todos los argumentos con espacio en blanco:
$ [ -f file ]
(Vemos a mucha gente que se confunde con este comportamiento; creen que [...] es «solo sintaxis» y que pueden omitir los espacios; [ es un comando y el espacio en blanco es obligatorio.)
Si nuestro nombre de archivo contiene espacios en blanco u otros caracteres especiales, también debe ir entre comillas:
$ [ -f "my file" ]
Bash entiende varios tipos diferentes de comandos: alias, funciones, builtins, palabras clave y ejecutables. Todos ellos son comandos y actúan de forma muy similar.
De forma predeterminada, los alias solo están habilitados en shells interactivos, no en scripts de shell. No los cubriremos aquí.
Una función contiene comandos de shell y se comporta de forma muy parecida a un script pequeño; incluso puede recibir argumentos y crear variables locales. Cuando se llama a una función, se ejecutan los comandos que contiene. Las funciones se tratarán en profundidad más adelante.
Bash tiene algunos comandos básicos integrados, como cd (cambiar de directorio), echo (escribir una salida), y así sucesivamente.
Se pueden considerar como funciones que ya vienen incluidas.
Las palabras clave son como los builtins, con la diferencia principal de que en realidad son sintaxis de bash y pueden analizarse con reglas especiales.
Por ejemplo, [ es un builtin de bash, mientras que [[ es una palabra clave de bash; ambos se usan para comprobar diversas condiciones.
Pero la palabra clave [[ tiene funcionalidad adicional.
Por ejemplo, proporciona el operador =~, que realiza comprobaciones con expresiones regulares.
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
Los operadores de comparación de strings < y > son otro ejemplo de cómo las palabras clave analizan el código de manera diferente.
Dentro de [[...]], estos operadores no necesitan ningún cuidado especial.
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
Pero dentro de [...] debes entrecomillarlos o escaparlos para evitar que el shell los analice como operadores de redirección.
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
El último tipo de comando que bash puede ejecutar es un ejecutable.
Son archivos ejecutables (programas) del sistema de archivos de tu computadora que puedes ejecutar.
(A los ejecutables también se los puede llamar comandos externos o aplicaciones.)
Por lo general, los ejecutables se invocan escribiendo solo su nombre; normalmente no se necesita la ruta completa del archivo.
Esto es posible porque bash usa una variable predefinida que lo ayuda a encontrar la ubicación de los archivos ejecutables comunes.
Esta variable se llama PATH.
Es un conjunto de nombres de directorios separados por dos puntos (por ejemplo, /usr/bin:/bin).
Cuando se especifica un comando (por ejemplo, myprogram o ls) sin una ruta de archivo (y no es un alias, una función, un builtin ni una palabra clave), bash busca en los directorios de PATH.
Los directorios se buscan en orden, de izquierda a derecha, para ver si contienen un ejecutable con el nombre dado.
Si el ejecutable está fuera de una ruta conocida, habrá que especificar la ruta del archivo del ejecutable.
Para el ejecutable myprogram en el directorio actual, usa ./myprogram; si está en el directorio /opt/somedirectory, usa /opt/somedirectory/myprogram.
Consejo: el comando type se puede usar para obtener detalles sobre un comando:
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
Un script es una secuencia de comandos guardada en un archivo. Bash lee el archivo y procesa los comandos en orden. Pasa al siguiente comando solo cuando el actual ha terminado. La excepción es si se ha especificado que un comando se ejecute de forma asíncrona (en segundo plano). Todavía no te preocupes demasiado por este caso; aprenderemos cómo funciona más adelante.
Cualquier ejemplo de línea de comandos de esta guía también se puede usar en un script.
Crear un script es fácil. Empieza creando un archivo nuevo y pon esto en la primera línea:
#!/bin/bash
Esta línea se llama directiva de intérprete (también se la llama hashbang o shebang).
Especifica que se debe usar /bin/bash como intérprete cuando el archivo se usa como ejecutable en un comando.
Cuando el kernel del sistema operativo ejecuta un archivo no binario, lee la primera línea del archivo.
Si la línea empieza con #!, el kernel usa esa línea para determinar a qué intérprete enviar el archivo.
(Hay otras formas válidas de hacer esto; consulta más abajo.)
El #! debe estar al principio mismo del archivo, sin espacios ni líneas en blanco antes.
Los comandos de nuestro script aparecerán en líneas separadas debajo de esta.
Consejo: en vez de #!/bin/bash , podrías usar
#!/usr/bin/env bash
env busca en $PATH el ejecutable que indica su primer argumento (en este caso, «bash»).
Para una explicación más detallada de esta técnica y de en qué se diferencia del simple #!/bin/bash, consulta esta pregunta en StackOverflow.
No te dejes engañar por scripts o ejemplos de internet que usan /bin/sh como intérprete.
¡sh no es bash!
Bash en sí es un shell «compatible con sh», lo que significa que puede ejecutar la mayoría de los scripts de sh y comparte gran parte de la misma sintaxis.
Sin embargo, lo contrario no es cierto; algunas características de bash fallan o causan un comportamiento inesperado en sh.
De hecho, no existe un único shell sh.
Hay muchas implementaciones de shell que admiten la sintaxis POSIX-sh.
Según tu sistema operativo, cuando ejecutas sh puede que en realidad estés ejecutando bash, ash, dash u otro shell.
Una vez creado el archivo del script, se puede ejecutar así:
$ bash myscript
En este ejemplo, ejecutamos el comando bash y le decimos que lea el script que le pasamos como argumento.
Cuando lo hacemos así, la línea #! es solo un comentario.
Como alternativa, podemos dar a nuestros scripts permisos de ejecución. Con este método podemos ejecutar el script como cualquier otro ejecutable en lugar de llamar a bash manualmente:
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
Cuando ejecutamos el comando de esta manera, el sistema operativo (SO) usa la línea #! para determinar qué intérprete debe usar para ejecutar tu script.
El contenido de esta lección proviene de Comandos y argumentos en la Bash Guide.