1. Operaciones y filtrado de archivos

La mayoría del trabajo de línea de comandos se realiza sobre archivos. En esta sección discutimos cómo mirar y filtrar el contenido de archivos, tomar la información necesaria de los archivos utilizando un único comando, y clasificar con facilidad el contenido de un archivo.

1.1. cat, tail, head, tee: Comandos de impresión de archivos

Estos comandos tienen casi la misma sintaxis: nombre_del_comando [opciones] [archivo(s)], y se pueden usar en una tubería. Todos se utilizan para imprimir parte de un archivo de acuerdo con ciertos criterios.

El utilitario cat concatena archivos imprimiendo los resultados en la salida estándar, la cual es por lo general la pantalla de su computadora. Este es uno de los comandos más ampliamente utilizados. Por ejemplo, puede usar:

# cat /var/log/mail/info

para imprimir, por ejemplo, el contenido del archivo de registro de un demonio de correo a la salida estándar[25]. El comando cat tiene una opción muy útil (-n) que le permite escribir los números de las líneas.

Algunos archivos, como los archivos de registro de los demonios (si es que están corriendo) por lo general tienen un tamaño enorme[26] y no es muy útil imprimirlos por completo en la pantalla. Por lo general Usted sólo necesita ver algunas líneas del archivo. Puede utilizar el comando tail para esto. El comando siguiente imprimirá, de manera predeterminada, las últimas 10 líneas del archivo /var/log/mail/info:

# tail /var/log/mail/info

Por lo general los archivos de registro varían dinámicamente debido a que el demonio asociado a dicho registro constantemente añade acciones y eventos al archivo de registro. Si desea mirar interactivamente los cambios al archivo de registro puede aprovechar la opción -f:

# tail -f /var/log/mail/info

En este caso, todos los cambios en el archivo /var/log/mail/info se imprimirán de inmediato en la pantalla. Utilizar el comando tail con la opción -f es muy útil cuando desea saber cómo funciona su sistema. Por ejemplo, mirando a través del archivo de registro /var/log/messages, puede estar al tanto con los mensajes del sistema y varios demonios.

Si utiliza a tail con más de un archivo, se imprimirá el nombre del archivo en una línea por separado antes de imprimir el contenido del mismo. Esto también funciona con la opción -f y es una valiosa adición para ver como interactúan las diferentes partes del sistema.

Puede usar la opción -n para mostrar las últimas N líneas de un archivo. Por ejemplo, para mostrar las últimas 2 líneas debería ingresar:

# tail -n2 /var/log/mail/info

Al igual que para los otros comandos, puede usar opciones diferentes a la vez. Por ejemplo, usando tanto la opción -n2 como la opción -f a la vez, comienza con las últimas dos líneas del archivo y sigue viendo las líneas nuevas a medida que se van escribiendo en el archivo de registro.

El comando head es similar a tail, pero imprime las primeras líneas de un archivo. El siguiente comando imprimirá, de manera predeterminada, las primeras 10 líneas del archivo /var/log/mail/info:

# head /var/log/mail/info

Al igual que con tail Usted puede usar la opción -n para especificar la cantidad de líneas a imprimir. Por ejemplo, para imprimir las primeras 2 ingrese:

# head -n2 /var/log/mail/info

También puede usar estos comandos juntos. Por ejemplo, si desea mostrar sólo las líneas 9 y 10, puede usar un comando donde primero el comando head va a seleccionar las primeras 10 líneas de un archivo y pasarlas a través de una tubería al comando tail.

# head /var/log/mail/info | tail -n2

La última parte seleccionará entonces las últimas 2 líneas y las imprimirá en la pantalla. De la misma manera, puede seleccionar la línea número 20, comenzando desde el final del archivo:

# tail -n20 /var/log/mail/info | head -n1

En este ejemplo, le decimos a tail que seleccione las últimas 20 líneas y las pase por una tubería a head. Luego, el comando head imprime la primer línea de los datos obtenidos.

Supongamos que deseamos imprimir el resultado del último ejemplo en la pantalla y guardarlo en el archivo resultados.txt. El utilitario tee nos puede ayudar. La sintaxis del mismo es:

tee [opciones] [archivo]

Ahora podemos cambiar el comando anterior de esta manera:

# tail -n20 /var/log/mail/info | head -n1 | tee resultados.txt

Tomemos otro ejemplo. Deseamos seleccionar las últimas 20 líneas, guardarlas en el archivo resultados.txt, pero imprimir en pantalla sólo la primera de las 20 líneas seleccionadas. Entonces, deberíamos teclear:

# tail -n20 /var/log/mail/info | tee resultados.txt | head -n1

El comando tee posee una opción útil (-a) que le permite añadir datos a un archivo existente.

En la próxima sección veremos cómo podemos utilizar el comando grep como filtro para separar los mensajes de Postfix de aquellos mensajes que provienen de otros servicios.

1.2. grep: Ubicar cadenas de caracteres en archivos

Ni el acrónimo (“General Regular Expression Parser”, Analizador General de Expresiones Regulares), ni el nombre son muy intuitivos, pero su uso es simple. grep busca el patrón pasado como argumento en uno o más archivos. La sintaxis es:

grep [opciones] <patrón> [uno o más archivos]

Si se mencionan varios archivos, los nombres de los mismos precederán a cada línea que muestra los resultados que se corresponden con el criterio de búsqueda. Use la opción -h para ocultar estos nombres; use la opción -l para obtener sólo los nombres de archivo en los cuales se cumple la condición de búsqueda. El patrón es una expresión regular, aunque generalmente consiste en una palabra simple. Las opciones usadas más frecuentemente son las siguientes:

  • -i: realizar una búsqueda que ignore la capitalización. (es decir, que ignore la diferencia entre las mayúsculas y las minúsculas);

  • -v: búsqueda inversa. Mostrar las líneas que no se corresponden con el patrón;

  • -n: mostrar, para cada línea encontrada, el número de línea;

  • -w: le dice a grep que el patrón debe corresponderse con una palabra completa, es decir debe aparecer tal cual y no como parte de otra palabra.

Volvamos entonces a analizar el archivo de registro del demonio de correo. Deseamos encontrar todas las líneas en el archivo /var/log/mail/info que contengan el patrón “postfix”. Entonces tecleamos este comando:

# grep postfix /var/log/mail/info

Si deseamos encontrar todas las líneas que NO contienen el patrón “postfix”, deberíamos usar la opción -v:

# grep -v postfix /var/log/mail/info

El comando grep se puede utilizar en una tubería.

Supongamos que deseamos encontrar todos los mensajes acerca de correos enviados satisfactoriamente. En este caso tenemos que filtrar todas las líneas que fueron añadidas al archivo de registro por el demonio de correo (contiene el patrón postfix) y deben contener un mensaje acerca del envío satisfactorio (status=sent)[27]:

# grep postfix /var/log/mail/info | grep status=sent

En este caso se utiliza a grep dos veces. Esto está permitido, pero no es muy elegante. Podemos obtener el mismo resultado utilizando el utilitario fgrep. En realidad fgrep es una forma más fácil de invocar a grep -F. Primero, debemos crear un archivo que contiene los patrones escritos en una columna. Se puede crear tal archivo de la manera siguiente (usamos el nombre patrones.txt):

# echo -e 'status=sent\npostfix' > ./patrones.txt

Verifique los resultados con el programa cat. \n es un patrón especial que significa “línea nueva”.

Luego llamamos al comando siguiente donde usamos el archivo patrones.txt con una lista de patrones y el utilitario fgrep en vez de la “doble llamada” a grep:

# fgrep -f ./patrones.txt /var/log/mail/info

El archivo ./patrones.txt puede tener tantos patrones como Usted desee. Por ejemplo, para seleccionar los mensajes acerca de los envíos satisfactorios de correo a peter@mandriva.com, será suficiente añadir esta dirección electrónica en nuestro archivo ./patrones.txt ejecutando el comando siguiente:

# echo 'peter@mandriva.com' >> ./patrones.txt

Está claro que puede combinar grep con tail y head. Si deseamos encontrar los mensajes sobre los últimos correos enviados a peter@mandriva.com, excepto el último, tecleamos:

# fgrep -f ./patrones.txt /var/log/mail/info | tail -n2 | head -n1

Aquí aplicamos el filtro descrito arriba y colocamos el resultado en una tubería para los comandos tail y head. Los mismos seleccionan los últimos valores de los datos, excepto el último.

1.3. Expresiones regulares y filtrado: egrep

Con grep estamos limitados con patrones y datos fijos ¿Cómo podríamos encontrar todos los correos electrónicos enviados a cada empleado de la “Empresa ABC”? Listar todos los correos electrónicos de los mismos no sería una tarea fácil ya que alguno podría escaparse o se debería buscar a mano en el archivo de registro.

Al igual que con fgrep, grep tiene un atajo al comando grep -E: egrep. El mismo toma como parámetro expresiones regulares en vez de patrones, brindando así una interfaz más potente para tratar texto.

Además de lo que mencionamos en Sección 3, “Patrones de englobamiento del shell” cuando hablamos de patrones de englobamiento, a continuación tiene algunas expresiones regulares más:

  • [:alnum:], [:alpha:] y [:digit:] se pueden usar en vez de definir las clases de caracteres y representan, respectivamente, todas las letras más todos los dígitos, todas las letras (mayúsculas y minúsculas), y todos los dígitos. Es más: dichas expresiones incluyen a los caracteres internacionales y respetan la localización del sistema.

  • [:print:] representa a todos los caracteres que se pueden imprimir en la pantalla.

  • [:lower:] y [:upper:] representan a todas las letras minúsculas y a todas las mayúsculas.

Hay más clases disponibles y puede verlas a todas en egrep(1). Las arriba mencionadas con las usadas con más frecuencia.

Una expresión regular puede estar seguida por uno o más operadores de repetición:

?

El elemento precedente es opcional, es decir: coincide ninguna o una vez, pero no más de una vez.

*

El elemento precedente coincide ninguna o más veces.

+

El elemento precedente coincide una o más veces.

{n}

El elemento precedente coincide exactamente n veces.

{n,}

El elemento precedente coincide n o más veces.

{n,m}

El elemento precedente coincide al menos n veces, pero no más de m veces.

Si pone una expresión regular entre paréntesis después la puede recuperar. Digamos que especificó la expresión [:alpha:]+. Puede que represente una palabra. Si desea detectar palabras que ocurren dos veces puede poner la expresión entre paréntesis y volver a usarla con \1 si este es el primer grupo. Puede tener hasta 9 de estas “memorias”.

$ echo -e "abc def\nabc abc def\nabc1 abc1\nabcdef\nabcdabcd\nabcdef abcef" > archivo_prueba
$ egrep "([[:alpha:]]+) \1" archivo_prueba
abc abc def
$
[Nota]Nota

Los caracteres [ y ] son parte del nombre del grupo por lo que hay que incluirlos para usar esa clase de caracteres. El primer [ dice que se va a usar un grupo de caracteres, el segundo es parte del nombre de ese grupo, y luego están los caracteres ] que cierran, correspondientes.

La única línea que se devuelve es aquella que coincidió exclusivamente con dos grupos de letras separados por un espacio. Ningún otro grupo coincidió con la expresión regular.

También puede usar el caracter | para hacer coincidir la expresión a la izquierda del | o la expresión a la derecha del mismo. Es un operador que une dichas expresiones. Usando el mismo archivo archivo_prueba creado antes, puede intentar buscar sólo expresiones que contienen palabras dobles o contienen palabras dobles con números:

$ egrep "([[:alpha:]]+) \1|([[:alpha:][:digit:]]+) \2" archivo_prueba
abc abc def
abc1 abc1
$

Note que para el segundo grupo que usa paréntesis se tuvo que utilizar \2, de lo contrario no hubiera coincidido con lo que se deseaba. Una expresión más eficiente sería, en este caso en particular:

$ egrep "([[:alnum:]]+) \1" archivo_prueba
abc abc def
abc1 abc1
$

Finalmente para hacer coincidir ciertos caracteres tiene que “escaparlos”, precediéndolos con una contrabarra. Dichos caracteres son: ?, +, {, |, (, ) y por supuesto \. Para hacer coincidir con ellos se debe escribir: \?, \+, \{, \|, \(, \) y \\.

Este truco simple lo puede ayudar a que evite teclear palabras repetidas en “su su” texto.

Las expresiones regulares en todas las herramientas deberían seguir estas reglas, o reglas muy similares. Dedicar algo de tiempo a entender estas reglas lo ayudará un montón con las otras herramientas tales como sed. sed le permite, entre otras cosas, manipular texto, cambiándolo usando expresiones regulares como reglas.

1.4. wc: Contando elementos en archivos

El comando wc (Word Count, Cuenta de palabras) se usa para calcular la cantidad de líneas, palabras y caracteres en archivos. También es útil para computar la longitud de la línea más larga. Su sintaxis es:

wc [opciones] [archivo(s)]

Las siguientes opciones son útiles:

  • -l: imprimir la cantidad de líneas;

  • -w: imprimir la cantidad de palabras;

  • -m: imprimir la cantidad total de caracteres;

  • -c: imprimir la cantidad de bytes;

  • -L: imprimir la longitud de la línea más larga en el texto.

El comando wc imprime la cantidad de líneas nuevas, palabras y caracteres de manera predeterminada. Aquí tiene algunos ejemplos de uso:

Si deseamos encontrar la cantidad de usuarios en nuestro sistema, podemos teclear:

$wc -l /etc/passwd

Si deseamos saber la cantidad de CPUs en nuestro sistema, tecleamos:

$grep "model name" /proc/cpuinfo | wc -l

En la sección anterior obtuvimos una lista de mensajes acerca de los correos enviados satisfactoriamente a las direcciones listadas en nuestro archivo ./patrones.txt. Si deseamos saber la cantidad de dichos mensajes, podemos enviar los resultados de nuestro filtro por una tubería al comando wc:

# fgrep -f ./patrones.txt /var/log/mail/info | wc -l

1.5. sort: Clasificando el contenido de los archivos

Aquí tiene la sintaxis de este poderoso utilitario de clasificación[28]:

sort [opciones] [archivo(s)]

Consideremos clasificar parte de el archivo /etc/passwd. Como puede ver este archivo no está clasificado:

$ cat /etc/passwd

Deseamos clasificarlo por el campo login. Entonces tecleamos:

$ sort /etc/passwd

El comando sort clasifica datos de manera ascendente comenzando por el primer campo (en nuestro caso, el campo login) de manera predeterminada. Para clasificar los datos de manera descendente, use la opción -r:

$ sort -r /etc/passwd

Cada usuario tiene su propio UID escrito en el archivo /etc/passwd. Clasifiquemos un archivo de manera ascendente con el campo UID:

$ sort /etc/passwd -t":" -k3 -n

Aquí utilizamos las siguientes opciones de sort:

  • -t":": le dice a sort que el símbolo : es el separador de campos;

  • -k3: significa que la clasificación debe hacerse según la tercer columna;

  • -n: dice que la clasificación ocurrirá sobre datos numéricos, no alfabéticos.

Se puede hacer lo mismo de manera inversa:

$ sort /etc/passwd -t":" -k3 -n -r

Note que sort tiene dos opciones importantes:

  • -u: realiza una clasificación estricta: los campos de clasificación duplicados se descartan;

  • -f: ignorar capitalización (trata igual a las minúsculas y a las mayúsculas).

Finalmente, si deseamos encontrar el usuario con el mayor UID podemos usar el comando siguiente:

$ sort /etc/passwd -t":" -k3 -n | tail -n1

donde clasificamos al archivo /etc/passwd de manera ascendente de acuerdo a la columna UID, y enviamos el resultado por medio de una tubería al comando tail que imprime el primer valor de la lista clasificada.



[25] Algunos ejemplos de esta sección están basados en trabajo real con archivos de registro de algunos servidores (servicios, demonios). Debe asegurarse que syslogd (permite el registro de los demonios) y el demonio correspondiente (en este ejemplo Postfix) estén activos, y que Usted trabaja como root. Por supuesto, siempre puede aplicar nuestros ejemplos a otros archivos.

[26] Por ejemplo, el archivo /var/log/mail/info contiene información acerca de todos los correos enviados, mensajes acerca de la recuperación de correo por parte de los usuarios con el protocolo POP, etc.

[27] Aunque es posible filtrar sólo por el patrón de estado, por favor siga el ejemplo ya que la finalidad es mostrarle un comando nuevo.

[28] Discutimos a sort brevemente aquí. Se podrían escribir libros completos acerca de sus características.