Introducción
En los dos artículos anteriores vimos una colección de filtros que manipularían datos para nosotros. En este artículo veremos cómo podemos unirlos para hacer una manipulación de datos más poderosa.
Hay un poco de lectura involucrada en esta sección. Aunque los mecanismos y su uso son bastante simples, es importante comprender varias características sobre su comportamiento si deseas utilizarlos de manera efectiva.
¿Qué son?
Cada programa que ejecutamos en la línea de comandos tiene automáticamente tres flujos de datos conectados.
- STDIN (0): entrada estándar (datos introducidos en el programa)
- STDOUT (1): Salida estándar (datos impresos por el programa, por defecto en la terminal)
- STDERR (2): Error estándar (para mensajes de error, también predeterminados para la terminal)
Las tuberías y la redirección son los medios por los cuales podemos conectar estos flujos entre programas y archivos para administrar los datos de manera interesante y útil.
Demostraremos las tuberías y la redirección a continuación con varios ejemplos, pero estos mecanismos funcionarán con cada programa en la línea de comandos, no solo los que hemos usado en los ejemplos.
Redireccionando a un archivo
Normalmente, obtendremos nuestro resultado en la pantalla, lo cual es conveniente la mayor parte del tiempo, pero a veces podemos desear guardarlo en un archivo para guardarlo como un registro, usarlo en otro sistema o enviarlo a otra persona.
El operador mayor que (>) indica a la línea de comandos que deseamos que la salida del programa (o lo que sea que envíes a STDOUT) se guarde en un archivo en lugar de imprimirse en la pantalla. Veamos un ejemplo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
user@bash: ls barry.txt bob example.png firstfile foo1 video.mpeg user@bash: ls > myoutput user@bash: ls barry.txt bob example.png firstfile foo1 myoutput video.mpeg user@bash: cat myoutput barry.txt bob example.png firstfile foo1 myoutput video.mpeg user@bash: |
Vamos a desglosarlo:
- Línea 1 Comencemos por ver qué hay en nuestro directorio actual.
- Línea 3 Ahora ejecutaremos el mismo comando, pero esta vez usamos > para indicarle a la terminal que guarde la salida en el archivo myoutput. Notarás que no necesitamos crear el archivo antes de guardarlo. La terminal lo creará automáticamente si no existe.
- Línea 4 Como puedes ver, nuestro nuevo archivo ha sido creado.
- Línea 6 Echemos un vistazo a lo que se guardó allí.
Algunas observaciones
Notarás que, en el ejemplo anterior, la salida guardada en el archivo era un archivo por línea en lugar de toda una línea cuando se imprime en la pantalla. La razón de esto es que la pantalla tiene un ancho conocido y el programa puede formatear su salida para adaptarse a eso.
Cuando estamos redirigiendo, puede ser a un archivo, o podría estar en otro lugar, por lo que la opción más segura es formatearlo como una entrada por línea. Esto también nos permite manipular más fácilmente esos datos más adelante, ya que lo veremos más adelante en la página.
Truco
Al redirigir y usar tuberías, los datos reales siempre serán los mismos, pero el formato de esos datos puede ser ligeramente diferente al que normalmente se imprime en la pantalla. Mantén esto en mente.
También notarás que el archivo que creamos para guardar los datos también está en nuestra lista. La forma en que funciona el mecanismo, el archivo se crea primero (si aún no existe) y luego el programa se ejecuta y la salida se guarda en el archivo.
Guardar en un archivo existente
Si redirigimos a un archivo que no existe, se creará automáticamente para nosotros. Sin embargo, si guardamos en un archivo que ya existe, se borrará su contenido y se guardará la nueva salida.
1 2 3 4 5 6 7 8 9 10 11 12 |
user@bash: cat myoutput barry.txt bob example.png firstfile foo1 myoutput video.mpeg user@bash: wc -l barry.txt > myoutput user@bash: cat myoutput 7 barry.txt user@bash: |
En su lugar, podemos obtener los datos nuevos que se agregarán al archivo utilizando el operador doble mayor que (>>).
1 2 3 4 5 6 7 8 9 10 11 12 13 |
user@bash: cat myoutput 7 barry.txt user@bash: ls >> myoutput user@bash: cat myoutput 7 barry.txt barry.txt bob example.png firstfile foo1 myoutput video.mpeg user@bash: |
Redirigir desde un archivo
Si usamos el operador menor que (<), entonces podemos enviar datos de la otra manera. Leeremos los datos del archivo y los alimentaremos al programa a través de su flujo STDIN.
1 2 3 4 5 |
user@bash: wc -l myoutput 8 myoutput user@bash: wc -l < myoutput 8 user@bash: |
Muchos programas (como hemos visto en secciones anteriores) nos permiten proporcionar un archivo como argumento de línea de comandos y leerá y procesará el contenido de ese archivo. Dado esto, es posible que te preguntes por qué necesitaríamos usar este operador. El ejemplo anterior ilustra una diferencia sutil pero útil.
Notarás que cuando ejecutamos wc suministrando el archivo para procesar como argumento de línea de comandos, la salida del programa incluía el nombre del archivo que fue procesado. Cuando lo ejecutamos redirigiendo el contenido del archivo a wc, el nombre del archivo no se imprimió.
Esto se debe a que cada vez que usamos redirección o tuberías, los datos se envían de forma anónima. Entonces, en el ejemplo anterior, wc recibió algo de contenido para procesar, pero no tiene conocimiento de dónde vino, por lo que es posible que no imprima esta información. Como resultado, este mecanismo a menudo se usa para obtener datos auxiliares (que pueden no ser necesarios) para que no se impriman.
Podemos combinar fácilmente las dos formas de redireccionamiento que hemos visto hasta ahora en un solo comando, como se ve en el siguiente ejemplo.
1 2 3 4 |
user@bash: wc -l < barry.txt > myoutput user@bash: cat myoutput 7 user@bash: |
Redireccionando STDERR
Ahora veamos la tercera secuencia que es Error estándar o STDERR. Los tres flujos en realidad tienen números asociados (entre paréntesis en la lista en la parte superior de la página). STDERR es la secuencia número 2 y podemos usar estos números para identificar las secuencias.
Si colocamos un número antes del operador >, redirigirá esa secuencia (si no usamos un número, como lo hemos estado haciendo hasta ahora, el valor predeterminado es la secuencia 1).
1 2 3 4 5 6 7 8 |
user@bash: ls -l video.mpg blah.foo ls: cannot access blah.foo: No such file or directory -rwxr--r-- 1 cesar users 6 May 16 09:14 video.mpg user@bash: ls -l video.mpg blah.foo 2> errors.txt -rwxr--r-- 1 cesar users 6 May 16 09:14 video.mpg user@bash: cat errors.txt ls: cannot access blah.foo: No such file or directory user@bash: |
Tal vez deseamos guardar tanto la salida normal como los mensajes de error en un solo archivo. Esto se puede hacer redirigiendo la secuencia STDERR a la secuencia STDOUT y redirigiendo STDOUT a un archivo.
Redirigimos a un archivo primero y luego redirigimos el flujo de error. Identificamos la redirección a una secuencia colocando un & delante del número de secuencia (de lo contrario, redirigiría a un archivo llamado 1).
1 2 3 4 5 |
user@bash: ls -l video.mpg blah.foo > myoutput 2>&1 cat myoutput ls: cannot access blah.foo: No such file or directory -rwxr--r-- 1 cesar users 6 May 16 09:14 video.mpg user@bash: |
Tuberías
Hasta ahora hemos tratado de enviar datos hacia y desde archivos. Ahora veremos un mecanismo para enviar datos de un programa a otro. Se llama tubería y el operador que utilizamos es (|) (se encuentra arriba de la tecla de barra invertida (\) en la mayoría de los teclados).
Lo que este operador hace es alimentar la salida del programa a la izquierda como entrada al programa a la derecha. En el siguiente ejemplo, enumeraremos solo los primeros 3 archivos en el directorio.
1 2 3 4 5 6 7 |
user@bash: ls barry.txt bob example.png firstfile foo1 myoutput video.mpeg user@bash: ls | head -3 barry.txt bob example.png user@bash: |
Podemos usar tuberías en tantos programas juntos como queramos. En el siguiente ejemplo, hemos usado tuberías en la salida a la cola para obtener solo el tercer archivo.
1 2 3 |
user@bash: ls | head -3 | tail -1 example.png user@bash: |
Truco
Cualquier argumento de línea de comandos que proporcionemos para un programa debe estar al lado de ese programa.
Truco
A menudo encuentro que las personas intentan y escriben sus tuberías de una vez y cometen un error en algún lugar a lo largo de la línea. Luego piensan que está en un punto, pero de hecho es otro punto.
Pierden mucho tiempo tratando de solucionar un problema que no está allí mientras no ven el problema que está allí. Si construyes tus tuberías gradualmente, no caerás en esta trampa. Ejecuta el primer programa y asegúrate de que proporcione el resultado que esperabas. Luego agrega el segundo programa y verifica nuevamente antes de agregar el tercero y así sucesivamente. Esto te ahorrará mucha frustración.
También puedes combinar tuberías y redireccionamiento.
1 2 3 4 |
user@bash: ls | head -3 | tail -1 > myoutput user@bash: cat myoutput example.png user@bash: |
Más ejemplos
A continuación, te presento algunos ejemplos más para dar una idea del tipo de cosas que puedes hacer con las tuberías. Hay muchas cosas que puedes lograr con las tuberías y estas son solo algunas de ellas.
Con experiencia y un poco de pensamiento creativo, estoy seguro de que encontrarás muchas más formas de utilizar las tuberías para facilitarle la vida.
Todos los programas utilizados en los ejemplos son programas que hemos visto antes. Sin embargo, he utilizado algunos argumentos de línea de comandos que aún no hemos cubierto.
Busca las páginas de manual relevantes para averiguar qué hacen. También puedes probar los comandos tú mismo, aumentando gradualmente para ver exactamente qué está haciendo cada paso.
En este ejemplo, estamos ordenando la lista de un directorio para que todos los directorios aparezcan primero.
1 2 3 4 5 6 |
user@bash: ls -l /etc | tail -n +2 | sort drwxrwxr-x 3 nagios nagcmd 4096 Mar 29 08:52 nagios drwxr-x--- 2 news news 4096 Jan 27 02:22 news drwxr-x--- 2 root mysql 4096 Mar 6 22:39 mysql ... user@bash: |
En este ejemplo alimentaremos menos la salida de un programa en el programa para que podamos verlo más fácilmente.
1 2 |
user@bash: ls -l /etc | less (Full screen of output you may scroll. Try it yourself to see.) |
Identifiquemos todos los archivos en tu directorio de inicio para los cuales el grupo tiene permiso de escritura.
1 2 3 |
user@bash: ls -l ~ | grep '^.....w' drwxrwxr-x 3 cesar users 4096 Jan 21 04:12 dropbox user@bash: |
Crear una lista de cada usuario que posee un archivo en un directorio dado, así como cuántos archivos y directorios posee.
1 2 3 4 5 6 |
user@bash: ls -l /projects/ghosttrail | tail -n +2 | sed 's/\s\s*/ /g' | cut -d ' ' -f 3 | sort | uniq -c 8 anne 34 harry 37 tina 18 ryan user@bash: |
Resumen
Lo que aprendimos
>
Guardar salida en un archivo.
>>
Agregar salida a un archivo.
<
Leer la entrada de un archivo.
2>
Redirigir mensajes de error.
|
Envía la salida de un programa como entrada a otro programa.
Conceptos importantes
Flujos
Cada programa que puedes ejecutar en la línea de comando tiene 3 secuencias, STDIN, STDOUT y STDERR.
Practicando lo aprendido
Analicemos algunos datos:
- En primer lugar, experimenta guardando la salida de varios comandos en un archivo. Sobrescribe el archivo y añádelo también. Asegúrate de utilizar rutas absolutas y relativas a medida que avanzas.
- Ahora observa si puedes incluir solo el vigésimo último archivo en el directorio /etc.
- Finalmente, observa si puedes obtener un recuento de cuántos archivos y directorios tienen permisos de ejecución en tu directorio de inicio.