CLI Linux: Aprenda a pensar en sed, awk y grep

0
1097

Redirección

Antes de que podamos hablar sobre sedawkgrep, necesitamos hablar sobre algo un poco más básico: la redirección de la línea de comandos. Nuevamente, vamos a mantener esto muy simple:

OperadorFunciónEjemplo
;Procese el comando de la derecha una vez que haya terminado de procesar el comando de la izquierda.echo one ; echo two
>Coloque la salida de la cosa a la izquierda en el  archivo vacío nombrado a la derecha.ls /home/me > myfilesonce.txt ; ls /home/me > myfilesonce.txt
>>Agregue la salida de la cosa a la izquierda al final del  archivo existente  a la derecha.ls /home/me > myfilestwice.txt ; ls /home/me >> myfilestwice.txt
<Utilice el archivo de la derecha como entrada estándar del comando de la izquierda.cat < sourcefile > targetfile
|Canalice la salida estándar de la cosa de la izquierda a la entrada estándar de la cosa de la derecha.echo “test123” | mail -s “subjectline” emailaddress

Comprender estos operadores de redireccionamiento es crucial para comprender los tipos de líneas de comando mágicas que presumiblemente está aquí para aprender. Permiten tratar las utilidades individuales y simples como parte de un todo mayor.

¡Y ese último concepto, dividir una tarea compleja en varias tareas más simples, es igualmente necesario para aprender a  pensar en invocaciones complejas de línea de comandos en primer lugar!

Busca “cadenas/strings” con Grep

grep es la herramienta que se utiliza para buscar líneas que contienen un string en particular .

Por ejemplo, supongamos que está interesado en encontrar qué puertos tiene abiertos el navegador web apache en su sistema. Muchas empresas de servicios públicos pueden lograr este objetivo; netstat es una de las opciones más antiguas y conocidas. Normalmente, invocamos  netstat usando los argumentos:  -anp para all sockets, numeric visualización y visualización de la propiedad pid de cada socket.

Desafortunadamente, esto produce una  gran cantidad de resultados, con frecuencia, varias decenas de páginas. Podría simplemente canalizar toda esa salida para que pueda leerla una página a la vez, con  netstat -anp | less. O, en su lugar puede redirigir a un archivo que se abrirá con un editor de texto: netstat -anp > netstat.txt.

Pero hay una mejor opción. En su lugar, podemos usar grep para devolver solo las líneas que realmente queremos. En este caso, lo que queremos saber es el servidor web apache. Entonces:

me@test:~$ sudo netstat -anp | head -n5
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 192.168.188.1:53        0.0.0.0:*               LISTEN      5128/dnsmasq        
tcp        0      0 192.168.254.1:53        0.0.0.0:*               LISTEN      5057/dnsmasq        
tcp        0      0 192.168.122.1:53        0.0.0.0:*               LISTEN      4893/dnsmasq        

me@test:~$ sudo netstat -anp | wc -l
1694

me@test:~$ sudo netstat -anp | grep apache
tcp6       0      0 :::80                   :::*                    LISTEN      4011/apache2  

me@test:~$ sudo netstat -anp | head -n2 ; sudo netstat -anp | grep apache
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp6       0      0 :::80                   :::*                    LISTEN      4011/apache2  
 

Introdujimos algunos comandos nuevos arriba:, headque limita la salida a las primeras  n líneas y luego la trunca. También está  wc, que, con el argumento -l, le dice cuántas líneas de texto llegan a su entrada estándar.

Entonces podemos traducir los cuatro comandos anteriores a un inglés simple:

  1. sudo netstat -anp | head -n5 : “Busque todos los sockets de red abiertos, pero que limite la salida a las primeras cinco líneas”.
  2. sudo netstat -anp | wc -l : “Encuentra todos los sockets de red abiertos, luego dime cuántas líneas de texto en total hay”.
  3. sudo netstat -anp | grep apache : “Encuentra todos los sockets de red abiertos, pero solo muéstrame los resultados que incluyan la palabra ‘apache'”.
  4. sudo netstat -anp | head -n2 ; sudo netstat -anp | grep apache : “Encuentra todos los sockets de red abiertos, pero muéstrame solo las dos líneas de encabezado, luego hazlo de nuevo, pero muéstrame solo los resultados de ‘apache'”.

Una vez que se sienta cómodo con el uso del comando grep para encontrar cadenas simples como se vio anteriormente, puede realizar tareas mucho más complejas. Estos incluyen, pero no se limitan a: no distingue entre mayúsculas y minúsculas, patrones más complejos (incluidas las expresiones regulares completas), exclusión (muéstrame solo las líneas que  no incluyen el patrón) y mucho, mucho más. Pero no se preocupe por eso hasta  después de que esté familiarizado con simples grepusos. Una vez que comienzas, ¡es realmente difícil imaginar la vida sin grep más!

Sed  reemplaza “cadenas/strings”

Ahora que sabe cómo limitar la salida a líneas coincidentes (o no coincidentes), el siguiente paso es aprender a cambiar esa salida sobre la marcha. Para ello, sedStream EDitor será su herramienta preferida.

Para usarlo sed, debe comprender al menos un poco acerca de las expresiones regulares (regexes). Una vez más, vamos a ignorar la gran mayoría de lo que pueden hacer las expresiones regulares y nos centraremos en lo más inmediato e intuitivo y útil: el simple reemplazo de patrones.

Digamos que desea cambiar todas las instancias de dog a snake en un montón de texto:

me@test:~$ echo "I love my dog, dogs are great!"
I love my dog, dogs are great!

me@test:~$ echo "I love my dog, dogs are great!" | sed 's/dog/snake/'
I love my snake, dogs are great!

me@test:~$ echo "I love my dog, dogs are great!" | sed 's/dog/snake/g'
I love my snake, snakes are great!

Podemos traducir estos tres comandos a un lenguaje sencillo:

  1. diga “I love my dog, dogs are great!”
  2. diga “I love my dog, dogs are great!” pero cambie la  primera instancia de dogsnake.
  3. diga “I love my dog, dogs are great!” pero cambie  todas las instancias de dog a snake.

Aunque en realidad solo trabajamos con texto sin formato, en realidad sed piensa en expresiones regulares. Desempaquetemos la expresión regular s/dog/snake/g: significa  buscar sed en la entrada de las instancias de dog y reemplazarlas con snake y hacerlo globally. Sin el g al final, sed solo hace un único reemplazo por línea de texto, como vemos en el comando # 2.

Muy bien, ahora que entendemos las expresiones regulares más simples posibles, ¿para qué podríamos usar sed en una línea de comandos del mundo real? Volvamos a nuestro primer ejemplo, en el que buscamos sockets de red abiertos pertenecientes a apache. Esta vez, digamos que queremos saber qué programa abrió un socket en el puerto 80:

me@test:~$ sudo netstat -anp | grep ::80
tcp6       0      0 :::80                   :::*                    LISTEN      4011/apache2  

me@test:~$ sudo netstat -anp | grep ::80 | sed 's/.*LISTEN *//'
4011/apache2  

En el primer comando, buscamos cualquier línea que contenga la cadena ::80, lo que nos limita al programa que se ejecuta en el puerto HTTP estándar. En el segundo, hacemos lo mismo, pero descartamos toda la información antes del PID y mutex (nombre para mostrar) del proceso que posee ese socket.

En el lenguaje de expresiones regulares, . es un carácter especial que coincide con cualquier carácter individual y * es un carácter especial que coincide con cualquier  secuencia de los caracteres precedentes. Por tanto, .* significa “coincidir con cualquier número de caracteres” y *(un espacio seguido de un asterisco) significa “coincidir con cualquier número de espacios”.

Awk encuentra  columnas

Una vez que se sienta cómodo con sed y grep, comenzará a sentirse como un superhéroe, hasta que se dé cuenta de lo difícil que es obtener solo la información relevante de una sola columna en el medio de una línea. Ahí es donde awk entra en juego.

Vale la pena señalar que awk es incluso más complejo (y capaz) que cualquiera de los dos sed o lo que grep fueron; de hecho awk técnicamente podría reemplazar ambos sed y grep  en la mayoría de los casos de uso.

Eso es porque en realidad awk es un lenguaje de scripting completo, no solo una herramienta. Por ahora solo vamos a pensar que awk es un extractor de columnas. Una vez más, volveremos a nuestro ejemplo con netstat. ¿Qué pasa si queremos saber en  qué puerto se está ejecutando Apache?

me@test:~$ sudo netstat -anp | grep apache
tcp6       0      0 :::80                   :::*                    LISTEN      4011/apache2    

me@test:~$ sudo netstat -anp | grep apache | awk '{print $4}'
:::80

me@test:~$ sudo netstat -anp | grep apache | awk '{print $4, $7}'    
:::80 4011/apache2

Una vez más, traduciremos nuestros ejemplos a un lenguaje sencillo:

  1. Encuentre todos los sockets abiertos y los programas que los poseen, pero limite la salida a los que tienen el texto ‘apache’ en ellos.
  2. Encuentre todos los sockets abiertos y los programas que los poseen, pero limite la salida a aquellos con el texto ‘apache’ en ellos, y limite  esa salida a la cuarta columna tabular solamente.
  3. Encuentre todos los sockets abiertos y los programas que los poseen, pero limite la salida a aquellos con el texto ‘apache’ en ellos, y limite  esa salida a la cuarta y séptima columnas tabulares.

Dado que awk es un idioma completo, su sintaxis puede parecer un poco torturada. Debe encapsular sus argumentos entre comillas simples, luego entre “llaves {}”, y debe usar la palabra clave print además de los números de sus columnas, veamos un ejemplo.

me@test:~$ cat awktext.txt
1 2 3
4 5 6
7 8 9

me@test:~$ cat awktext.txt | awk '{SUM+=$2}END{print SUM}'
15

me@test:~$ cat awktext.txt | awk '{SUM+=$2}END{print SUM/NR}'
5

En los ejemplos anteriores, agregamos el valor de la columna especificada – la segunda columna, especificada con $2 – a una variable que nombramos SUM. Después de sumar el valor de la columna 2 en cada fila SUM, podemos generar la salida SUMdirectamente o dividirla por una variable especial predefinida NR, que significa “Número de filas”.

Y sí, si se lo está preguntando, awk maneja bien los decimales, así como también cantidades variables de espacios en blanco entre columnas:

me@test:~$ cat awktext.txt
1.0 2.1 3.2 4.3
5.4 6.5 7.6 8.7
9.8 0.9 1.0 2.1

me@test:~$ cat awktext.txt | awk '{SUM+=$2}END{print SUM/NR}'
3.16667

me@test:~$ cat awktext.txt
1.0     2.1  3.2  4.3
5.4 6.5      7.6       8.7
9.8 0.9 1.0 2.1

me@test:~$ cat awktext.txt | awk '{SUM+=$2}END{print SUM/NR}'
3.16667

Como siempre, le  recomiendo encarecidamente que se sienta cómodo con el uso más básico de awk—encontrar datos en qué columna se encuentra en lugar de intentar buscarlos identificando el texto antes y después— antes de preocuparse por sus usos más sofisticados y complejos.

Conclusión

Con un poco de suerte, ahora que ha visto los usos más comunes y menos complejos de estas tres herramientas icónicas, estará listo para comenzar a  pensar en ellas. Ahora que lo sabe, se dará cuenta rápidamente de que su utilidad surge una y otra vez en el mundo real.

Antes de aprender a pensar en sedawkgrep, en general, he logrado las mismas tareas por grandes volúmenes de salida de comando en un editor de texto.