Guía Práctica para Controlar y Optimizar stdout 🐧


En esta sección, exploraremos cómo manipular la salida de información en sistemas operativos Linux/Unix. Esta habilidad es esencial para administradores de sistemas, Pentesters, Programadores y cualquier persona que use sistemas basados en Unix como su plataforma principal. Aprender a gestionar y filtrar el flujo de datos en la terminal puede optimizar tareas, facilitar análisis y mejorar la eficiencia en múltiples escenarios.


Control de stdout

Descripción General: La salida estándar, conocida como stdout, es el flujo de datos que generan los programas en la línea de comandos. Es una de las tres corrientes de E/S (entrada/salida) predeterminadas en sistemas Unix y similares, junto con stdin (entrada estándar) y stderr (salida de error estándar). Al ejecutar un comando en la terminal, la salida que ves en pantalla se envía, por defecto, a stdout.

Puedes redirigir stdout a un archivo o a otro programa utilizando operadores como > y |. Esto te permite almacenar la salida o encadenar comandos para realizar tareas complejas. Por ejemplo:

echo "Hola, mundo!" > hola.txt

Este comando enviará el texto “Hola, mundo!” al archivo hola.txt en lugar de mostrarlo en pantalla.

Objetivo: Esta guía tiene como objetivo enseñarte a utilizar comandos de Linux/Unix para redirigir, filtrar y manipular la salida de datos (stdout) de forma eficiente, optimizando tu flujo de trabajo y facilitando el procesamiento de información en la terminal.

Redirección y Pipes

Para redirigir la salida de un comando a un archivo específico, usamos el operador >. Este operador envía el resultado de un comando a un archivo que elijamos, sobrescribiendo el contenido si ya existe. Por ejemplo:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ echo "Hola Mundo" > hola.txt

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ cat hola.txt
Hola Mundo

Si queremos añadir información al archivo en lugar de sobrescribirlo, utilizamos >>, que agrega el nuevo contenido al final del archivo. Veamos un ejemplo:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ echo "Soy 7heAnsw3r" >> hola.txt

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ cat hola.txt
Hola Mundo
Soy 7heAnsw3r

De esta forma, podemos redirigir y acumular la salida de varios comandos en un solo archivo, lo cual es útil para almacenar y organizar datos sin perder información anterior.

Uso de Pipes (|)

Los pipes (|) son otra herramienta esencial en la línea de comandos de Linux. Permiten redirigir la salida de un comando como entrada de otro, lo cual facilita el procesamiento de datos de forma secuencial y permite crear flujos de trabajo complejos sin necesidad de archivos temporales.

Algunas ventajas de los pipes incluyen:

  • Procesamiento Secuencial: Permiten ejecutar comandos uno tras otro.
  • Simplificación de Procesos: Hacen posible crear flujos de trabajo complejos con un solo comando.
  • Evitación de Archivos Temporales: No es necesario guardar datos intermedios en archivos.
  • Transferencia Continua de Datos: Los datos fluyen de un comando al siguiente sin interrupción.

Ejemplo básico de uso de pipe:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ echo "Hola Mundo" | grep "Hola"
"Hola" Mundo

Uso de Grep

El comando grep es una herramienta poderosa en la línea de comandos que permite buscar palabras o cadenas de texto en archivos y salidas de comandos. Como vimos en el ejemplo anterior, grep muestra las líneas completas que contienen la información buscada. Su versatilidad y eficacia la convierten en una herramienta esencial para administradores de sistemas, desarrolladores y cualquier persona que trabaje con texto.

A continuación, se presenta una guía detallada sobre las opciones más comunes de grep, acompañada de ejemplos prácticos que ilustran su uso en diferentes situaciones.

En el ejemplo anterior, utilizamos pipes para trabajar con grep, pero en esta ocasión veremos cómo utilizar grep sin pipes. En este ejemplo, podemos observar que al buscar la palabra ‘hola’, no obtendremos resultados; sin embargo, al buscar la palabra ‘Hola’, sí encontraremos la coincidencia correspondiente. Esto se debe a que grep es sensible a mayúsculas y minúsculas (case sensitive).

Aquí tienes un ejemplo:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ grep 'hola' hola.txt

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ grep 'Hola' hola.txt
Hola Mundo

Opciones de Control de Salida

  • Ignorar Distinciones: En el ejemplo anterior, observamos que al buscar con mayúsculas o minúsculas se obtienen diferentes resultados. Para ignorar estas distinciones, podemos utilizar la opción -i. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -i 'hola' hola.txt
      'Hola' Mundo
    
  • Mostrar Números de Línea: Si queremos ver el número de línea donde se encuentra cada coincidencia en el archivo, utilizamos la opción -n. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -in 'hola' hola.txt
      1:'Hola' Mundo
    
  • Mostrar Solo Coincidencias: Para ver únicamente las coincidencias sin mostrar el resto de la línea, utilizamos la opción -o. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -ion 'hola' hola.txt
      1:Hola
    
  • Resaltar Coincidencias: Para resaltar las coincidencias en color, podemos usar la opción --color. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -i --color 'hola' hola.txt
      'Hola' Mundo
    

Opciones de Selección e Interpretación de Patrones

  • Extended Regular Expressions (-E): Usar expresiones regulares extendidas

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -E 'Ho|So' hola.txt
      'Ho'la Mundo
      'So'y 7heAnsw3r
    
  • Basic Regular Expressions (-G): Usa expresiones regulares básicas

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -G 'M.n' hola.txt
      Hola 'Mu'ndo
    
  • Fixed Strings (-F): Trata los patrones como cadenas Fijas

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -F 'Hola' hola.txt
      'Hola' Mundo
    
  • Patrones desde Archivos (-f): Usa patrones desde un archivo

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ cat patterns.txt 
      Hola
      Soy
      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -f patterns.txt hola.txt 
      Hola Mundo
      Soy 7heAnsw3r
    

Además de las opciones anteriores, grep ofrece varias opciones interesantes que pueden ser muy útiles en diferentes situaciones:

  • Invertir Coincidencias (-v): Esta opción permite mostrar las líneas que no coinciden con el patrón especificado. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -v 'hola' hola.txt
      Hola Mundo
      Soy 7heAnsw3r
    
  • Mostrar Contexto (-C): Muestra un número específico de líneas de contexto antes y después de cada coincidencia. Por ejemplo, -C 2 mostrará 2 líneas de contexto:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -C 2 'Hola' hola.txt
      Hola Mundo
      Soy 7heAnsw3r
      Estoy utilizando grep
    
  • Contar Coincidencias (-c): Esta opción cuenta el número de líneas que coinciden con el patrón en lugar de mostrar las líneas. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -c 'hola' hola.txt
      0
    
  • Buscar Recursivamente (-r): Permite buscar el patrón en todos los archivos dentro de un directorio y sus subdirectorios. Por ejemplo:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -r 'Hola' .
      ./hola.txt:Hola Mundo
      ./patterns.txt:Hola
    
  • Mostrar Líneas Después de la Coincidencia (-A): Esta opción permite mostrar un número específico de líneas después de cada coincidencia. Por ejemplo, -A 3 mostrará 3 líneas después:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -A 3 'hola' hola.txt
    
  • Mostrar Líneas Antes de la Coincidencia (-B): Similar a -A, pero muestra líneas antes de cada coincidencia. Por ejemplo, -B 2 mostrará 2 líneas antes:

      ┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
      └─$ grep -B 2 'hola' hola.txt
    

Estas opciones permiten a los usuarios personalizar su búsqueda y manejar la salida de manera más efectiva, haciendo de grep una herramienta aún más poderosa en la línea de comandos.

AWK

¿Qué es awk?

awk es una poderosa herramienta de procesamiento de texto en sistemas Unix/Linux que permite analizar y manipular datos de texto mediante patrones y acciones. Su diseño es especialmente útil para trabajar con archivos de texto estructurados en columnas, facilitando la extracción y transformación de datos.

Principales Características de awk

  • Procesamiento de Campos: Divide las líneas de un archivo en campos utilizando delimitadores (por defecto, el espacio en blanco), lo que permite acceder y manipular datos de manera eficiente.

  • Patrones y Acciones: Ejecuta acciones específicas en líneas que coinciden con un patrón determinado, ofreciendo flexibilidad en la manipulación de datos.

  • Variables Incorporadas: awk incluye variables integradas como $0 (que representa la línea completa) y $1, $2, etc. (que representan campos específicos), lo que simplifica el acceso a los datos.

  • Funciones Incorporadas: Proporciona una variedad de funciones para trabajar con cadenas, realizar operaciones numéricas e incluso ejecutar cálculos complejos, ampliando su capacidad de análisis.

  • Portabilidad: awk es compatible con casi todos los sistemas Unix/Linux, lo que lo convierte en una herramienta versátil y ampliamente utilizada en la administración y análisis de datos.

Ejemplo básico de awk: Procesando la salida de netstat

Comenzaremos ejecutando un comando que genera una gran cantidad de información organizada en columnas: netstat. Este comando muestra las conexiones de red activas en el sistema. Para procesar esta salida, utilizaremos pipes junto con awk para extraer información específica.

Ejemplo de netstat sin awk:

netstat

La salida mostrará algo similar a esto:

Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  3      [ ]         STREAM     CONNECTED     20243    
unix  3      [ ]         STREAM     CONNECTED     15443    /run/systemd/journal/stdout
unix  3      [ ]         STREAM     CONNECTED     12950    /run/dbus/system_bus_socket
unix  3      [ ]         DGRAM      CONNECTED     8789     
unix  3      [ ]         STREAM     CONNECTED     17572    
unix  3      [ ]         STREAM     CONNECTED     15035    /run/user/1000/bus

Ejemplo de netstat con awk:

Podemos utilizar awk para filtrar la salida y mostrar solo la columna que nos interesa. Por ejemplo, para mostrar el estado de las conexiones, ejecutamos:

netstat | awk '{print $5}'

Esto mostrará:

STREAM
STREAM
STREAM
DGRAM
STREAM
STREAM
STREAM
STREAM
STREAM

Filtrando usuarios en /etc/passwd

En las pruebas de pentesting, es crucial verificar la integridad del archivo /etc/passwd, que contiene información sobre las cuentas de usuario.

Ejemplo básico:

cat /etc/passwd

Esto mostrará una salida extensa, por lo que podemos utilizar awk para filtrar la información y mostrar solo los usuarios válidos:

cat /etc/passwd | awk -F ":" '{print $1}'

Salida:

root
daemon
bin
sys
sync
games

Imprimiendo múltiples columnas

Si queremos imprimir más de una columna, simplemente especificamos los números de las columnas que deseamos imprimir. Por ejemplo, para mostrar el nombre de usuario, ID de usuario y ID de grupo, podemos hacer:

cat /etc/passwd | awk -F ":" '{print $1, $3, $4}'

Esto imprimirá:

root 0 0
daemon 1 1
bin 2 2
sys 3 3
sync 4 65534
games 5 60

Si queremos agregar tabulaciones entre los campos, podemos utilizar "\t":

cat /etc/passwd | awk -F ":" '{print $1 "\t" $3 "\t" $4}'

Esto mostrará:

root    0    0
daemon  1    1
bin     2    2
sys     3    3
sync    4    65534
games   5    60

Configurando delimitadores personalizados

Podemos establecer delimitadores personalizados utilizando la sección BEGIN en awk. Por ejemplo:

cat /etc/passwd | awk 'BEGIN {FS=":"; OFS=" ; "} {print $1, $3, $4}'

Esto nos dará:

root ; 0 ; 0
daemon ; 1 ; 1
bin ; 2 ; 2
sys ; 3 ; 3
sync ; 4 ; 65534
games ; 5 ; 60

Usando expresiones regulares

Para imprimir el último campo delimitado por / de una salida, podemos usar la siguiente expresión regular:

awk -F "/" '/^\// {print $NF}' /etc/shells

Aquí, cada parte del comando hace lo siguiente:

  • -F "/": Establece / como el delimitador.
  • /^\//: Coincide con líneas que comienzan con /.
  • {print $NF}: Imprime el último campo (donde NF es el número total de campos en la línea).

Al ejecutar este comando, obtenemos:

bash
bash
bash
dash
pwsh
screen
tmux
zsh

Comprendiendo BEGIN y NF

  • BEGIN: Es una sección especial en awk que se ejecuta antes de procesar cualquier línea de entrada. Se usa comúnmente para establecer variables de campo (FS) y otras configuraciones globales.

  • NF: Esta variable integrada representa el número total de campos en la línea actual. Por ejemplo, si deseas acceder al último campo sin saber cuántos campos hay, puedes utilizar $NF.

Usando NF para imprimir campos

Si no sabemos cuántos campos hay, podemos utilizar NF para acceder a campos específicos:

cat /etc/passwd | awk -F ":" '{print $NF}'

Esto imprimirá el último campo de cada línea, que generalmente contiene la ruta del intérprete de comandos.

Operar con Líneas de Texto Mediante el Comando awk

Hasta ahora, hemos aprendido varios métodos para imprimir columnas específicas de texto. Pero también podemos filtrar líneas completas según lo que necesitemos. ¡Vamos a ver cómo hacerlo!

Imprimir Líneas que Sigan un Patrón Específico

Imagina que tienes la salida del comando df, que se ve así:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ df   
Filesystem     1K-blocks     Used Available Use% Mounted on
udev             6109492        0   6109492   0% /dev
tmpfs            1234568     1392   1233176   1% /run
/dev/sda1      101639152 21191960  75238004  22% /
tmpfs            6172832    11692   6161140   1% /dev/shm
tmpfs               5120        0      5120   0% /run/lock
tmpfs               1024        0      1024   0% /run/credentials/systemd-journald.service
tmpfs               1024        0      1024   0% /run/credentials/systemd-tmpfiles-setup-dev-early.service

Si solo quieres mostrar las líneas que comienzan con /, puedes usar el siguiente comando:

df | awk '/^\// {print}'

Esto te dará solo las líneas que empiezan por /:

/dev/sda1      101639152 21191960  75238004  22% /

Ejemplo Extra: Si quieres filtrar aún más y mostrar solo la línea que comienza por /dev/sda1, ejecutarías:

df | awk '/^\/dev\/sda1/ {print}'

Filtrar Líneas que Empiezan o Terminan con un Patrón

Ya hemos visto cómo mostrar líneas que empiezan con una letra o palabra. Si quieres ver las líneas que empiezan por tmpfs, simplemente haz:

df | awk '/^tmpfs/ {print}'

Para ver solo las líneas que terminan en /shm, puedes usar:

df | awk '/\/shm$/ {print}'

Y si quieres líneas que empiecen con tmpfs y terminen con /shm, puedes hacer esto:

df | awk '/\/shm$/ && /^tmpfs/ {print}'

Mostrar Columnas Específicas de Líneas Filtradas

En lugar de mostrar todas las columnas, puedes especificar cuáles quieres ver. Por ejemplo, para mostrar solo las columnas 1, 2 y 3 de las líneas que empiezan por /, harías lo siguiente:

df -h | awk '/^\// {print $1"\t"$2"\t"$3}'

Ejemplo de Sumar Columnas:

Si quieres sumar las columnas 2 y 3:

df -h | awk '/^\// {print $1"\t"$2 + $3}'

Filtrar Líneas por Longitud

Si quieres imprimir las líneas del archivo /etc/shells que tienen más de 9 caracteres, el comando es:

awk 'length($0) > 9' /etc/shells

Puedes usar length($0) < 9, length($0) == 9 o length($0) != 9 para otros filtros.

Filtrar con Múltiples Condiciones

Usa el operador && para mostrar líneas que cumplan múltiples condiciones. Por ejemplo, si quieres mostrar líneas de df -h que empiecen con t y cuya columna 6 tenga más de 8 caracteres:

sudo df -h | awk '/^t/ && length($6) > 8 {print $0}'

Ver Longitud de Líneas

Para saber cuántos caracteres tiene cada línea en /etc/shells, haz:

awk '{print length}' /etc/shells

Si también quieres ver el contenido:

awk '{print length"\t"$0}' /etc/shells

Y para obtener el número de caracteres de la columna 1 en df -h, usa:

sudo df -h | awk '{print length($1)"\t"$1}'

Filtrar por Último Delimitador

Para listar procesos cuyo último delimitador sea firefox, puedes combinar lo aprendido con if:

ps -ef | awk '{ if($NF == "obsidian") print $0}'

Si solo quieres el número de proceso y el último delimitador:

ps -ef | awk '{ if($NF == "obsidian") print $2" "$NF}'

Operar con Texto Mediante el Comando sed

Hasta ahora, hemos aprendido varios métodos para manipular texto en la terminal. Hoy, nos centraremos en sed, una poderosa herramienta para editar texto de manera no interactiva. ¡Vamos a ver cómo funciona!

Reemplazar Texto

Uno de los usos más comunes de sed es reemplazar texto en un archivo. Supongamos que tienes un archivo llamado texto.txt con el siguiente contenido:

Hola, mundo.
Esto es un archivo de ejemplo.

Para la creacion pongamos en practica todo lo que hemos aprendido hasta aqui de la siguiente manera:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ echo "Hola, mundo.\nEsto es un archivo de ejemplo." > texto.txt 
┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ cat texto.txt 
Hola, mundo.
Esto es un archivo de ejemplo.

Si quieres reemplazar “mundo” por “universo”, utilizarías el siguiente comando:

sed 's/mundo/universo/' texto.txt

Esto imprimirá:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ sed 's/mundo/universo/' texto.txt
Hola, universo.
Esto es un archivo de ejemplo.

Lo cual no altera el archivo en si pero si a la salida del archivo

Si deseas que el cambio se aplique a todas las líneas, simplemente usa la opción g:

sed 's/mundo/universo/g' texto.txt

Eliminar Líneas

Puedes usar sed para eliminar líneas específicas. Por ejemplo, si deseas eliminar la segunda línea de texto.txt, ejecutarías:

sed '2d' texto.txt

Esto imprimirá:

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ sed '2d' texto.txt               
Hola, mundo.
┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ cat texto.txt 
Hola, mundo.
Esto es un archivo de ejemplo.

Si quieres eliminar todas las líneas que contengan la palabra “ejemplo”, usa:

sed '/ejemplo/d' texto.txt

Insertar y Añadir Texto

También puedes insertar o añadir texto en líneas específicas. Para insertar una línea antes de la segunda línea:

sed '2i Esta línea se inserta antes de la segunda línea.' texto.txt
┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ sed '2i Esta línea se inserta antes de la segunda línea.' texto.txt
Hola, mundo.
Esta línea se inserta antes de la segunda línea.
Esto es un archivo de ejemplo.
┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ cat texto.txt
Hola, mundo.
Esto es un archivo de ejemplo.

Para añadir una línea después de la primera línea, utiliza:

sed '1a Esta línea se añade después de la primera línea.' texto.txt

Reemplazo en Archivos

Para hacer los cambios directamente en el archivo, usa la opción -i. Esto reemplazará “mundo” por “universo” en el archivo original:

sed -i 's/mundo/universo/g' texto.txt

Ten en cuenta que este comando no imprimirá nada en la salida estándar.

Filtrar Líneas por Patrón

Puedes usar sed para mostrar solo líneas que coincidan con un patrón. Si quieres mostrar solo las líneas que contienen la palabra “archivo”:

sed -n '/archivo/p' texto.txt

La opción -n suprime la salida normal, y p imprime las líneas coincidentes.

┌──(7heAnsw3r㉿AllEyezOnMe)-[~]
└─$ sed -n '/archivo/p' texto.txt
Esto es un archivo de ejemplo.

Modificar Delimitadores

Por defecto, sed utiliza la barra / como delimitador, pero puedes cambiarlo a otro carácter. Por ejemplo, si quieres reemplazar : por - en un archivo fechas.txt:

sed 's#:#-#g' fechas.txt

Ejemplos Combinados

Combina varias operaciones en un solo comando. Por ejemplo, para reemplazar “mundo” por “universo” y eliminar la segunda línea:

sed -e 's/mundo/universo/g' -e '2d' texto.txt

Usar sed con Tubos

Puedes usar sed en combinación con otros comandos a través de pipes. Por ejemplo, para filtrar la salida de ps y reemplazar “bash” por “sh”:

ps aux | sed 's/bash/sh/g'

Esta guía es un recurso completo sobre cómo utilizar comandos esenciales como grep, awk, sed, pipes y redirecciones. Estas herramientas son de gran utilidad para administradores de sistemas, equipos de blue team y red team, así como para cualquier persona que trabaje en entornos sin interfaz gráfica, como en servidores, donde el uso de GUI puede ser costoso y menos eficiente. Aquí encontrarás una introducción práctica sobre cómo mejorar y manejar la salida de texto con estos comandos, haciendo tu trabajo más fluido y optimizado.

En el próximo capítulo, exploraremos herramientas adicionales como less, more y tee, y también veremos la generación de scripts para automatizar tareas diarias.

via GIPHY