Curso de Bash desde cero / Capítulo 4
Comandos avanzados
Lectura de archivos
cat archivomuestra todo el contenido de golpe. Útil para archivos pequeños; con uno grande te llenará la pantalla.less archivomuestra el contenido paginado, que es lo que quieres para archivos largos. Se sale pulsandoq.more archivoes comoless, pero con menos posibilidades (por ejemplo, no deja volver hacia atrás). Hoy se usaless.head archivomuestra las primeras 10 líneas.head -n 20 archivopara elegir cuántas.tail archivomuestra las últimas 10 líneas.tail -n 20 archivopara elegir cuántas.tail -f registro.logdeja el archivo abierto y va mostrando lo nuevo según se escribe. Es la forma habitual de vigilar un log en directo. Se corta conCtrl + C.
Consejo
Para desplazarte por la paginación es habitual usar flechas, barra espaciadora, scroll o PgUp/PgDown.
Búsqueda: grep
grep busca un texto dentro de archivos, o dentro de lo
que le llegue de otro comando. Devuelve las líneas
enteras en las que aparece.
grep "error" registro.log # líneas que contienen "error"Las opciones que más se usan:
| Opción | Qué hace |
|---|---|
-i |
No distingue mayúsculas de minúsculas |
-r |
Busca dentro de todo un directorio, recursivamente |
-n |
Muestra el número de línea de cada resultado |
-v |
Al revés: muestra las líneas que NO contienen el texto |
-c |
En vez de las líneas, cuenta cuántas hay |
-l |
Solo dice en qué archivos aparece, no muestra las líneas |
-w |
Solo la palabra entera (grep -w red no encuentra
«redes») |
Y se combinan como cualquier otra opción:
grep -rn "TODO" . # dónde y en qué línea, por todo el proyecto
grep -ic "error" *.log # cuántos errores por archivo, sin distinguir
# mayúsculas
grep -v "^#" config.txt # el archivo sin sus líneas de comentarioConsejo
Ese
^#del último ejemplo es una expresión regular: el^significa «al principio de la línea». Congrepbastan tres símbolos para el 90% de los casos:
^principio de línea$final de línea.un carácter cualquiera
Recuento: wc
wc (word count) cuenta lo que le eches.
| Opción | Cuenta |
|---|---|
| (ninguna) | líneas, palabras y bytes |
-l |
líneas |
-w |
palabras |
-c |
bytes |
-m |
caracteres (distinto de bytes si hay tildes o eñes) |
wc -l notas.txt # cuántas líneas tiene
wc -lw notas.txt # líneas y palabrasLos tres canales de un comando
Antes de redirigir nada hay que saber qué se redirige. Todo comando en Linux tiene tres canales abiertos, y cada uno tiene un número:
| Nº | Nombre | Qué es |
|---|---|---|
| 0 | stdin | Por donde entra la información |
| 1 | stdout | Por donde sale el resultado normal |
| 2 | stderr | Por donde salen los mensajes de error |
La clave está en que el resultado y los errores salen por sitios distintos, aunque los veas mezclados en pantalla. Eso es justo lo que permite separarlos:
ls /carpeta_que_no_existe > salida.txtEl archivo salida.txt queda vacío y el
error sigue apareciendo en pantalla. Porque > redirige
solo el canal 1, y el error va por el 2.
Redirecciones
| Símbolo | Qué hace |
|---|---|
> |
Manda la salida a un archivo, sobrescribiéndolo |
>> |
Manda la salida a un archivo, añadiendo al final |
< |
Coge la entrada de un archivo |
2> |
Manda solo los errores a un archivo |
2>&1 |
Manda los errores al mismo sitio que la salida |
&> |
Manda las dos cosas a la vez (atajo de
> archivo 2>&1) |
echo "texto" > notas.txt # crea o sobrescribe notas.txt
echo "más texto" >> notas.txt # añade una línea al final
sort < notas.txt # ordena el contenido del archivo
ls /noexiste 2> errores.txt # solo el error va al archivo
ls /noexiste &> todo.txt # salida y error, los dos al archivoPrecaución
>borra el archivo antes de escribir, sin avisar. Si te equivocas y escribes>donde querías>>, te has cargado el contenido anterior. Cuando dudes, usa>>.
El agujero negro: /dev/null
/dev/null es un archivo especial que se traga
todo lo que le mandes. Sirve para silenciar lo que no te
interesa:
comando 2>/dev/null # ejecuta y no me enseñes los errores
comando >/dev/null # ejecútalo y no me enseñes el resultado
comando &>/dev/null # ejecútalo en silencio absolutoAviso
Ojo con el orden en
2>&1: significa «manda el canal 2 donde está ahora el canal 1». Por eso va después de la redirección:
comando > archivo 2>&1guarda las dos cosas en el archivo. Correcto.comando 2>&1 > archivoNO hace lo mismo: cuando se lee2>&1el canal 1 todavía apunta a la pantalla, así que los errores se quedan en pantalla y solo la salida va al archivo.
Pipes
El símbolo | conecta la salida de un comando con
la entrada del siguiente, sin pasar por ningún archivo. Es lo
que hace de Bash una herramienta potente: comandos pequeños que se
encadenan.
cat notas.txt | grep "bash" | wc -lSe lee de izquierda a derecha: muestra el archivo, quédate con las líneas que contienen «bash», y cuenta cuántas son.
Algunos encadenados que se usan a diario:
ls -l | grep "^d" # solo los directorios
history | grep git # qué comandos de git he usado
ps aux | grep node # procesos de node en marcha
cat registro.log | tail -n 50 # las últimas 50 líneasConsejo
Los pipes solo conectan el canal 1. Si quieres que también pasen los errores, usa
|&(o2>&1 |).
Variables de entorno
Puedes definir variables temporalmente (locales) o que existan en todo momento (globales).
Variable de shell
Vive solo en la terminal donde la creaste. Si desde ahí lanzas otro programa, ese programa no la ve.
nombre_variable=“valor asociado a la variable” Almacena el valor de un texto en una variable.
echo $nombre_variable Muestra el valor de la variable.
Variable de entorno
Se crea con export. La diferencia es que la
heredan los programas que lances desde esa terminal: pasa a
formar parte de su entorno.
Aviso
«Exportar» una variable no la hace permanente. Cuando cierras la terminal desaparece igual que las demás. Lo único que consigues con
exportes que los programas hijos puedan verla.Para que exista siempre hay que escribirla en un archivo de configuración, como se explica un poco más abajo.
Se ve muy claro con un ejemplo:
saludo="hola" # variable de shell
export despedida="adiós" # variable de entorno
# bash -c lanza una terminal hija:
bash -c 'echo "saludo=[$saludo] despedida=[$despedida]"'
# saludo=[] despedida=[adiós]La hija no ve saludo, pero sí
despedida.
Algunas variables de entorno que ya existen
echo $HOME # /home/nica -> tu directorio personal
echo $USER # nica -> tu nombre de usuario
echo $PWD # /home/nica -> el directorio en el que estás
echo $PATH # /usr/local/bin:/usr/bin:/bin: ...$PATH merece una parada. Es la lista de carpetas
donde Bash busca los programas, separadas por dos puntos.
Cuando escribes ls, Bash recorre esas carpetas por orden
hasta encontrar un ejecutable llamado ls.
Por eso, para lanzar un script tuyo que está en el directorio actual
hay que escribir ./script.sh y no script.sh a
secas: el directorio actual no está en el
PATH, y si lo estuviera sería un problema de seguridad
(bastaría con dejar un archivo llamado ls en una carpeta
para engañarte).
Crear una variable de entorno
export NOMBRE_VARIABLE="su valor"Que sobreviva al cierre de la terminal
Hay que escribir esa línea en un archivo de configuración, que Bash lee solo al arrancar. Verás mencionados cuatro:
| Archivo | Cuándo se lee |
|---|---|
~/.bashrc |
Cada vez que abres una terminal normal |
~/.bash_profile |
Solo al iniciar sesión (por ejemplo, por SSH) |
~/.bash_login |
Alternativa al anterior, si aquel no existe |
~/.profile |
El más antiguo; lo leen también otras shells |
Consejo
No te compliques: usa
~/.bashrc. Es el que se lee al abrir una terminal, que es el 95% de los casos. Los otros tres existen por razones históricas.
# Añadir la línea al final del archivo
echo 'export MI_VARIABLE="hola"' >> ~/.bashrc
# Aplicar los cambios sin cerrar la terminal
source ~/.bashrcsource (o su atajo .) hace que Bash
relea el archivo en la sesión actual. Si no, tendrías
que cerrar y volver a abrir la terminal.
Ejercicios
Muestra todo el contenido de un archivo.
Muestra el contenido paginado de un archivo.
Muestra las 15 primeras líneas de un archivo.
Muestra las 15 últimas líneas de un archivo.
Busca una palabra en un archivo.
Cuenta las líneas de un archivo.
Redirige una salida y guárdala en un archivo.
Añade una nueva salida al archivo anterior.
Encadena 3 comandos con pipes.
Crea una variable de shell y muéstrala.
Lanza
lssobre una carpeta que no exista redirigiendo con>a un archivo. ¿Qué contiene el archivo? ¿Dónde ha ido a parar el mensaje de error? Repítelo con2>y luego con&>.Busca una palabra en un archivo mostrando el número de línea, y después al revés: muestra las líneas que no la contienen.
Cuenta cuántas veces aparece una palabra en un archivo, encadenando
grepywc.Muestra tu
$PATHy cuenta cuántas carpetas tiene (pista: están separadas por:, ytr ':' '\n'las pone una por línea).