Bash
Gracias Sofi!!!!!!
Bash (Bourne Again Shell) es un intérprete de comandos y un lenguaje de scripting muy popular en sistemas operativos basados en Unix, como Linux y macOS. Su nombre, "Bourne Again Shell", es un guiño al Bourne Shell (sh
), el shell original creado por Stephen Bourne en los años 70, al que Bash mejora con varias características nuevas y útiles.
Características claves de Bash
Intérprete de comandos
Un intérprete de comandos es un programa que lee y ejecuta comandos ingresados por el usuario. En el contexto de Bash, este intérprete actúa como un puente entre el usuario y el sistema operativo, permitiéndote ejecutar tareas y controlar el sistema mediante comandos escritos en texto.
Funcionamiento básico
-
Entrada de Comandos: Cuando abrís una terminal en tu sistema, se inicia una sesión de Bash. Ahí podés escribir comandos que el intérprete lee línea por línea. Por ejemplo, podés escribir
ls
para listar los archivos en un directorio ocd
para cambiar de directorio. -
Procesamiento de Comandos: Bash recibe el comando, lo interpreta y lo convierte en acciones que el sistema operativo puede ejecutar. Por ejemplo, cuando escribís
mkdir nueva_carpeta
, Bash interpreta esto como una solicitud para crear un nuevo directorio llamado "nueva_carpeta". -
Ejecución: Después de interpretar el comando, Bash lo pasa al sistema operativo para que lo ejecute. El sistema operativo realiza la acción solicitada y luego Bash muestra el resultado o el output en la terminal.
-
Retorno de Resultados: Tras ejecutar el comando, Bash muestra el resultado de la operación en la terminal. Si el comando fue exitoso, verás los resultados esperados; si hubo un error, se mostrará un mensaje de error.
Entrada y salida en Bash
En Bash, la comunicación entre los procesos se maneja a través de tres flujos estándar: stdin
(entrada estándar), stdout
(salida estándar) y stderr
(error estándar). Estos flujos permiten la interacción entre el usuario, los comandos y el sistema operativo.
-
stdin (Entrada Estándar): Es el flujo a través del cual los comandos reciben datos. Por defecto,
stdin
proviene del teclado, pero podés redirigir la entrada desde un archivo o desde otro comando (se da explicación de estas redirecciones más adelante en este documento). -
stdout (Salida Estándar): Es el flujo a través del cual los comandos envían datos que deberían mostrarse al usuario. Por defecto,
stdout
se muestra en la terminal, pero podés redirigir la salida a un archivo. -
stderr (Error Estándar): Es el flujo a través del cual los comandos envían mensajes de error. Por defecto,
stderr
también se muestra en la terminal, pero podés redirigir los mensajes de error a un archivo separado.
Características avanzadas del intérprete de comandos
Expansiones de terminal
En Bash, las expansiones son una característica muy útil que permite cambiar algo que escribís por otra cosa antes de ejecutar el comando. Aunque la expansión cambia el comando, este reemplazo no se muestra explícitamente en la terminal.
Expansión de variables con $
Usar $
seguido del nombre de una variable o alias la expande y reemplaza con su valor. Esto también te permite ejecutar comandos dentro de otros comandos. Por ejemplo:
echo "El contenido del archivo es $(cat archivo.txt)"
En este caso, $(cat archivo.txt)
se expande para mostrar el contenido del archivo en lugar del comando en sí. También podés usar comillas invertidas `
en lugar de $(...)
para ejecutar comandos dentro de otros comandos.
Expansión con !
El signo !
reemplaza el texto por el último comando ejecutado que comience con lo que sigue al signo de exclamación. Esto es útil para reutilizar comandos anteriores sin tener que reescribirlos. Por ejemplo:
!ls
Esto ejecutará el último comando que comience con ls
. Podés leer más sobre esto en este artículo: https://blog.wemake.pe/posts/usando-el-signo-de-exclamacion-para-simplificar-los-comandos/.
-
!!
: Expande y ejecuta el último comando completo que fue ejecutado. Es muy útil cuando olvidás anteponersudo
a un comando que requiere permisos de root:sudo !!
Esto repetirá el último comando ejecutado, pero con sudo
al principio.
-
$?
: Esta expansión reemplaza con el código de salida del último programa ejecutado. Es útil para verificar si un comando se ejecutó correctamente (0 significa éxito). -
$@
: Esta variable especial expande cada argumento pasado al script como una cadena separada.$@
se utiliza comúnmente para iterar sobre todos los argumentos, como en un buclefor
. Cada argumento se considera como una cadena separada, por lo que$@
proporciona una lista de todos los argumentos pasados al script.
Protección de expansión con comillas simples y backslashes:
-
Si no querés que algo se expanda dentro de una cadena, podés usar comillas simples (
' '
). Todo lo que esté entre comillas simples no se expande.echo 'El valor de $nombre no se expandirá'
Además, podés usar un backslash (\
) antes de una expansión para evitar que ocurra. Esto es útil si querés mostrar un carácter especial literalmente:
echo \$nombre
- Operaciones aritméticas: Bash permite realizar operaciones aritméticas dentro de dobles paréntesis
(( ))
.
Redirección y pipelines
En Bash, las redirecciones y los pipelines son herramientas esenciales que te permiten controlar y manipular el flujo de datos entre comandos y archivos. Estos mecanismos son fundamentales para crear scripts eficientes, ya que te permiten encadenar comandos y redirigir su entrada y salida de manera flexible.
Redirección de salida (> y >>)
-
>
: Si utilizás>
después de un comando seguido del nombre de un archivo, lo que hace es enviar todo lo que el comando imprimiría en la terminal (su salida estándar) al archivo especificado. Sin embargo, si el archivo ya existe, lo sobreescribe completamente.echo "Hola Mundo" > saludo.txt
En este ejemplo, se crea un archivo llamado saludo.txt (si no existe) y se guarda el texto "Hola Mundo" en él. Si saludo.txt ya existía, su contenido anterior se perderá, ya que será sobrescrito por "Hola Mundo".
-
>>
: Si utilizás >>, realiza una función similar, pero en lugar de sobrescribir el archivo, agrega el contenido al final del archivo existente. Esto es útil cuando querés acumular información en un archivo.echo "Hola otra vez" >> saludo.txt
Acá, el texto "Hola otra vez" se agrega al final del archivo
saludo.txt
, preservando cualquier contenido previo.Nota: Ambos operadores (
>
y>>
) crearán el archivo si no existe.
Uso de cat
para copiar y concatenar archivos
Copiar archivos
Podés usar cat
(que concatena y muestra archivos) junto con >
para copiar el contenido de un archivo en otro. Por ejemplo:
cat archivo_original.txt > copia_archivo.txt
Este comando copiará el contenido de archivo_original.txt en copia_archivo.txt. Si copia_archivo.txt ya existe, será sobrescrito.
Concatenar archivos
Podés combinar varios archivos en uno solo utilizando cat
y >
:
cat archivo_1.txt archivo_2.txt > archivo_combinado.txt
Esto creará un nuevo archivo llamado archivo_combinado.txt que contendrá el contenido de ambos archivos, archivo_1.txt y archivo_2.txt.
Escritura interactiva con cat
Si ejecutás cat
seguido de >
y el nombre de un archivo sin especificar un archivo fuente, podés escribir libremente lo que querés guardar en ese archivo. Lo que escribas en la terminal se enviará al archivo hasta que presiones Ctrl+D
para indicar el fin de la entrada:
cat > notas.txt
- Al ejecutar este comando, lo que escribas a continuación se guardará en
notas.txt
.
Redirección de entrada (<
)
<
: Este operador hace lo opuesto al >
. En lugar de redirigir la salida de un comando a un archivo, toma el contenido de un archivo y lo usa como entrada para un comando.
wc -w < archivo.txt
En este ejemplo, wc -w cuenta las palabras en el archivo archivo.txt, utilizando el contenido del archivo como entrada para el comando wc.
Pipelines (|
)
|
: Los pipelines permiten conectar la salida de un comando directamente a la entrada de otro comando. Esto es extremadamente útil cuando querés procesar datos en varias etapas, utilizando múltiples comandos.
comando1 | comando2 | comando3
echo "Hola Mundo" | less
Aquí, el texto "Hola Mundo"
se envía como entrada al comando less
, que permite visualizar el contenido de manera paginada.
Otro ejemplo común es usar grep
para filtrar la salida de un comando:
ls -l | grep ".txt"
En ese caso, la salida del comando ls -l
(que lista archivos en detalle) se pasa al comando grep
, que filtra y muestra solo las líneas que contienen .txt
, es decir, archivos con esa extensión.
cat archivo.txt | wc -l
En este caso, el comando cat archivo.txt
se utiliza para visualizar el contenido del archivo denominado archivo.txt
. La salida de este comando se redirige al comando wc -l
, que cuenta el número total de líneas en el archivo.
cat archivo.txt | grep "hola"
Aquí, el comando cat archivo.txt
se emplea para mostrar el contenido de archivo.txt
. Este contenido se pasa al comando grep "hola"
, que filtra y muestra únicamente las líneas que contienen la palabra "hola".
cat archivo.txt | sort
En este ejemplo, el comando cat archivo.txt
presenta el contenido del archivo archivo.txt
. La salida es redirigida al comando sort, el cual organiza alfabéticamente las líneas del archivo y muestra el resultado ordenado.
El comando grep
es una herramienta de búsqueda en sistemas Unix/Linux que permite localizar líneas que coinciden con un patrón específico dentro de uno o varios archivos. Su nombre proviene de una abreviatura del comando de búsqueda en editores de texto en modo consola: "global regular expression print".
Ejemplos de uso:
- Buscar una palabra específica en un archivo:
grep "hola" ejemplo.txt
En este caso, el comando grep busca la palabra "hola" en el archivo ejemplo.txt y muestra todas las líneas que contienen dicha palabra.
- Buscar una palabra específica y luego refinar la búsqueda con otra palabra:
grep "hola" archivo1 | grep "busqueda2"
Primero, el comando grep
busca la palabra "hola" en el archivo archivo1. La salida de esta búsqueda se pasa a otro comando grep
que filtra las líneas que también contienen la palabra "busqueda2". El resultado final muestra solo las líneas que contienen ambas palabras.
- Buscar una palabra en todos los archivos de un directorio:
grep -l "palabra" ./*
En este ejemplo, la opción -l
del comando grep
indica que se deben listar únicamente los nombres de los archivos que contienen la palabra "palabra". La búsqueda se realiza en todos los archivos del directorio actual, indicado por ./*.
Lenguaje de Scripting
Además de ser un intérprete de comandos, Bash es un lenguaje de scripting completo. Los scripts de Bash son archivos de texto que contienen una secuencia de comandos Bash. Estos scripts pueden automatizar tareas repetitivas, realizar operaciones complejas y gestionar el sistema de manera más eficiente. Son muy útiles para tareas como la configuración del sistema, la gestión de archivos y la ejecución de tareas programadas.
- Creación de scripts: Para crear un script en Bash, escribís varios comandos en un archivo de texto. Los scripts comienzan típicamente con una línea especial llamada "shebang", que indica al sistema qué intérprete usar para ejecutar el script. La línea shebang tiene el formato:
#!/bin/bash
Esto le dice al sistema que ejecute el script usando Bash.
- Ejecución de scripts: Para ejecutar un script de Bash, usás el comando
./
seguido del nombre del script. Por ejemplo:
./mi_script.sh
Asegúrate de que el archivo del script tenga permisos de ejecución. Si no tiene permisos de ejecución, podés agregarlos con el comando chmod
:
chmod +x mi_script.sh
Variables y Redirección
Dentro de un script, podés usar variables de entorno y redirección de entrada/salida para manejar datos de manera más eficiente. Las variables te permiten almacenar valores temporales, y la redirección te ayuda a controlar dónde se envían los datos de entrada y salida.
Estructuras de Control
Bash soporta estructuras de control como if
, for
, while
, y case
, lo que permite construir scripts más complejos y dinámicos. Estas estructuras se desarrollarán con mayor detalle en otra sección.
Input del Usuario
Un script puede solicitar input del usuario usando el comando read
.
echo "Ingresa tu nombre:"
read nombre
echo "Hola, $nombre"
Variables
En Bash, las variables se utilizan para almacenar y manipular datos. Las variables pueden contener valores como cadenas de texto, números o resultados de comandos. Los valores almacenados en las variables pueden ser utilizados en comandos y scripts para realizar operaciones dinámicas, lo que proporciona gran flexibilidad en la automatización de tareas.
Definición de Variables
Para definir una variable en Bash, simplemente asignás un valor a un nombre de variable sin espacios entre el nombre, el signo igual, y el valor. Por ejemplo:
nombre="José"
edad=30
En este caso, nombre
es una variable que almacena la cadena "José", y edad
es una variable que almacena el número 30.
Acceso a Variables
Para acceder al valor almacenado en una variable, usás el signo $
(expansión de terminal) seguido del nombre de la variable. Por ejemplo:
echo "Mi nombre es $nombre y tengo $edad años."
Esto imprimirá en la terminal: Mi nombre es José y tengo 30 años.
Variables de Entorno
Además de las variables que definís en tus scripts, existen variables de entorno que son proporcionadas por el sistema operativo y están disponibles en todos los scripts y comandos. Algunas variables de entorno comunes son:
$HOME
: Contiene el directorio home del usuario actual.$PATH
: Contiene los directorios donde el sistema busca los comandos ejecutables.$USER
: Almacena el nombre del usuario actual.
Podés utilizar y modificar estas variables de entorno en tus scripts:
echo "El directorio home del usuario $USER es $HOME"
Asignación de Resultados de Comandos a Variables
Una gran característica de Bash es la capacidad de asignar el resultado de un comando a una variable. Esto se hace utilizando comillas invertidas (`) o $(...). Por ejemplo:
fecha_actual=$(date)
Aquí, la variable fecha_actual
contendrá la salida del comando date
, que es la fecha y hora actuales.
Variables Temporales
Podés definir variables temporales para usarlas solo dentro de un comando específico. Esto es útil para modificar temporalmente el entorno sin afectar al sistema globalmente:
NOMBRE="Carlos" echo "Hola, $NOMBRE"
En este caso, NOMBRE
se define como "Carlos"
solo para la ejecución del comando echo
.
Variables Especiales
Bash también tiene algunas variables especiales que son útiles en scripts:
$0
: El nombre del script.$1
,$2
, ...$9
: Los argumentos pasados al script.$#
: El número de argumentos pasados al script.$?
: El código de salida del último comando ejecutado.$$
: El ID del proceso del script en ejecución.
Por ejemplo, si ejecutás un script con:
./mi_script.sh arg1 arg2
Podes acceder a arg1
y arg2
dentro de un script utilizando $1
y $2
respectivamente.
Declaración de Variables de sólo Lectura
Si querés que una variable no pueda ser modificada después de su asignación, podés declararla como de sólo lectura usando el comando readonly
:
readonly PI=3.14159
Cualquier intento de modificar PI
después de su declaración resultará en un error.
Eliminar Variables
Podés eliminar una variable del entorno utilizando el comando unset
:
unset nombre
Después de esto la variable nombre
ya no estará definida.
Estructuras Condicionales e Iterativas
Bash proporciona varias estructuras de control que permiten gestionar el flujo de ejecución de comandos y scripts, adaptándose a diferentes situaciones y necesidades. Estas estructuras incluyen tanto condicionales como bucles, facilitando la toma de decisiones y la repetición de tareas en los scripts. A continuación, se describen las principales, junto con su sintaxis:
if
Esta estructura permite ejecutar un bloque de comandos si se cumple una determinada condición. Es fundamental para tomar decisiones en los scripts, permitiendo realizar distintas acciones según el resultado de una evaluación.
if [ condición ]; then
comandos
elif [ otra_condición ]; then
comandos
else
comandos
fi
if [ condición ]; then
: Comienza la estructura. La condición se evalúa y, si es verdadera, se ejecutan los comandos dentro del bloque.elif [ otra_condición ]; then
: Es opcional y permite añadir más condiciones.else
: Opcional. Los comandos dentro de este bloque se ejecutan si ninguna de las condiciones anteriores se cumple.fi
: Finaliza la estructuraif
.
Comparaciones en if
Números:
eq
: Igual (equal)ne
: No son iguales (not equal)gt
: Mayor que (greater than)lt
: Menor que (less than)ge
: Mayor o igual (greater or equal)le
: Menor o igual (less or equal)
Cadenas:
- Comparar si dos cadenas son iguales:
string1 == string2
- Comparar si dos cadenas son diferentes:
string1 != string2
- Verificar si una cadena coincide con una expresión regular:
string1 =~ regex
→ Devuelvetrue
- Verificar si la longitud de una cadena es cero:
-z
- Verificar si la longitud de una cadena no es cero:
-n
for
Utilizado para repetir un conjunto de comandos para cada elemento de una secuencia, como una lista de elementos o números.
for variable in secuencia; do
comandos
done
for variable in secuencia; do
: Inicia el bucle. La variable toma el valor de cada elemento en la secuencia uno por uno.comandos
: Los comandos que se ejecutan en cada iteración del bucle.done
: Finaliza el buclefor
.
while
Ejecuta comandos repetidamente mientras una condición sea verdadera. Es útil para bucles en los que no sabes cuántas iteraciones necesitarás de antemano.
while [ condición ]; do
comandos
done
while [ condición ]; do
: Comienza el bucle. Los comandos se ejecutan mientras la condición sea verdadera.comandos
: Los comandos que se ejecutan en cada iteración del bucle.done
: Finaliza el buclewhile
.- Todo lo que se puede hacer con un for se puede hacer con un while pero no necesariamente viceversa.
Case
Permite ejecutar comandos basados en el valor de una variable, similar a un switch
en otros lenguajes de programación. Es útil para manejar múltiples opciones de forma más ordenada.
case variable in
patrón1)
comandos ;;
patrón2)
comandos ;;
*)
comandos ;;
esac
case variable in
: Comienza la estructuracase
;variable
es el valor que se va a evaluar.patrón1)
: Define el primer patrón que se compara con el valor devariable
. Loscomandos
se ejecutan si hay coincidencia.*)
: El patrón*
actúa como un comodín para manejar cualquier caso no especificado anteriormente.esac
: Finaliza la estructuracase
.
Until
El bucle until ejecuta los comandos mientras la condición sea falsa. Termina cuando la condición se vuelve verdadera.
until [ condicion ]; do
comandos
done
Funciones
En Bash, las funciones permiten agrupar comandos relacionados y reutilizarlos en diferentes partes del script. Esto mejora la organización y modularidad del código, haciendo que sea más fácil de leer y mantener.
Definición de una función
Para definir una función en Bash, utilizamos la siguiente sintaxis:
nombre_funcion() {
# Comandos a ejecutar
}
Ejemplo de definición de función:
#!/bin/bash
saludar(){
echo "¡Hola, $1!"
}
En el ejemplo:
- saludar es el nombre de la función.
- $1 representa el primer argumento pasado a la función.
Llamar a una función
Después de definir una función, podés llamarla en cualquier parte del script posterior a su definición:
saludar "Daniel"
Esto imprimirá: ¡Hola, Daniel!
Retorno de valores
Aunque Bash no soporta el retorno de valores directamente como en otros lenguajes de programación, podemos utilizar echo
para devolver un valor y capturarlo con $()
:
Ejemplo de retorno de valores:
obtener_suma() {
local suma=$(( $1 + $2 ))
echo $suma
}
resultado=$(obtener_suma 5 10)
echo "La suma es: $resultado"
En este ejemplo, obtener_suma
devuelve la suma de dos números, y el valor es capturado en la variable resultado
.
Uso de variables locales
Dentro de una función, utiliza la palabra clave local
para definir variables que solo están disponibles dentro de esa función, evitando conflictos con variables globales:
Ejemplo de variables locales:
calcular_area() {
local base=$1
local altura=$2
local area=$(( base * altura ))
echo $area
}
area_rectangulo=$(calcular_area 5 10)
echo "El área del rectángulo es: $area_rectangulo"