103.4 Lección 1
Certificación: |
LPIC-1 (101) |
---|---|
Versión: |
5.0 |
Tema: |
103 Comandos GNU y Unix |
Objetivo: |
103.4 Usar flujos, tuberías y redireccionamientos |
Lección: |
1 de 2 |
Introducción
Todos los programas de computadora siguen el mismo principio general: los datos recibidos de alguna fuente se transforman para generar un resultado inteligible. En el contexto de shell de Linux, la fuente de datos puede ser un archivo local, un archivo remoto, un dispositivo (como un teclado), etc. La salida del programa generalmente se representa en una pantalla, pero también es común almacenar los datos de salida en un sistema de archivos local, enviarlo a un dispositivo remoto, reproducirlo a través de altavoces de audio, etc.
Los sistemas operativos inspirados en Unix, como Linux, ofrecen una gran variedad de métodos de entrada/salida. En particular, el método de descriptores de archivos permite asociar dinámicamente números enteros con canales de datos, de modo que un proceso pueda hacer referencia a ellos como sus flujos de datos de entrada/salida.
Los procesos estándar de Linux tienen tres canales de comunicación abiertos de manera predeterminada: el canal de entrada estándar (la mayoría de las veces llamado simplemente stdin), el canal de salida estándar (stdout) y el canal de error estándar (stderr). Los descriptores numéricos de archivos asignados a estos canales son 0
a stdin, 1
a stdout y 2
a stderr. Los canales de comunicación también son accesibles a través de los dispositivos especiales /dev/stdin
, /dev/stdout
y /dev/stderr
.
Estos tres canales de comunicación estándar permiten a los programadores escribir código que lee y escribe datos sin preocuparse por el tipo de medio del que proviene o al que va. Por ejemplo, si un programa necesita un conjunto de datos como entrada, solo puede solicitar datos de la entrada estándar y lo que se esté utilizando como entrada estándar proporcionará esos datos. Del mismo modo, el método más simple que un programa puede usar para mostrar su salida es escribirlo en la salida estándar. En una sesión de shell estándar, el teclado se define como stdin y la pantalla del monitor se define como stdout y stderr.
El shell Bash tiene la capacidad de reasignar los canales de comunicación al cargar un programa. Permite, por ejemplo, anular la pantalla como salida estándar y usar un archivo en el sistema de archivos local como stdout.
Redireccionamientos
La reasignación del descriptor de archivo de un canal en el entorno de shell se denomina redirect. Una redirección se define mediante un caracter especial dentro de la línea de comandos. Por ejemplo, para redirigir la salida estándar de un proceso a un archivo, el símbolo mayor que >
se coloca al final del comando y sigue la ruta al archivo que recibirá la salida redirigida:
$ cat /proc/cpuinfo >/tmp/cpu.txt
Por defecto, solo se redirige el contenido que llega a stdout. Eso sucede porque el valor numérico del descriptor de archivo debe especificarse justo antes del símbolo mayor que y, cuando no se especifica, Bash redirige la salida estándar. Por lo tanto, usar >
es equivalente a usar 1>
(el valor del descriptor de archivo stdout es 1
).
Para capturar el contenido de stderr, se debe usar la redirección 2>
en su lugar. La mayoría de los programas de línea de comandos envían información de depuración y mensajes de error al canal de error estándar. Es posible, por ejemplo, capturar el mensaje de error provocado por un intento de leer un archivo inexistente:
$ cat /proc/cpu_info 2>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory
Tanto stdout como stderr se redirigen al mismo objetivo con &>
o >&
. Es importante no colocar ningún espacio al lado del ampersand, de lo contrario, Bash lo tomará como la instrucción para ejecutar el proceso en segundo plano y no realizar la redirección.
El destino debe ser una ruta a un archivo en el que se pueda escribir, como /tmp/cpu.txt
, o un descriptor de archivo editable. Un objetivo de descriptor de archivo está representado por un ampersand seguido del valor numérico del descriptor de archivo. Por ejemplo, 1>&2
redirige stdout a stderr. Para hacer lo contrario, de stderr a stdout, se debe usar 2>&1
en su lugar.
Aunque no es muy útil, dado que hay una forma más corta de hacer la misma tarea, es posible redirigir stderr a stdout y luego redirigirlo a un archivo. Por ejemplo, una redirección para escribir stderr y stdout en un archivo llamado log.txt
puede escribirse como >log.txt 2>&1
. Sin embargo, la razón principal para redirigir stderr a stdout es permitir el análisis de mensajes de error y depuración. Es posible redirigir la salida estándar de un programa a la entrada estándar de otro, pero no es posible redirigir directamente el error estándar a la entrada estándar de otro programa. Por lo tanto, los mensajes del programa enviados a stderr primero deben redirigirse a stdout para que otros stdin puedan leerlos.
Para descartar la salida de un comando, su contenido se puede redirigir al archivo especial /dev/null
. Por ejemplo, >log.txt 2>/dev/null
guarda el contenido de stdout en el archivo log.txt
y descarta el stderr. El archivo /dev/null
puede ser escrito por cualquier usuario pero no se pueden recuperar datos, ya que no se almacenan en ningún lado.
Se presenta un mensaje de error si el destino especificado no se puede escribir (si la ruta apunta a un directorio o un archivo de solo lectura) y no se realiza ninguna modificación en el destino. Sin embargo, una redirección de salida sobrescribe el destino existente sin ninguna confirmación. Los archivos se sobrescriben mediante redireccionamientos de salida a menos que la opción Bash noclobber
esté habilitada, lo que se puede hacer para la sesión actual con el comando set -o noclobber
o set -C
:
$ set -o noclobber $ cat /proc/cpu_info 2>/tmp/error.txt -bash: /tmp/error.txt: cannot overwrite existing file
Para desactivar la opción noclobber
para la sesión actual, ejecute set +o noclobber
o set +C
. Para que la opción noclobber
sea persistente, debe incluirse en el perfil Bash del usuario o en el perfil de todo el sistema.
Incluso con la opción noclobber
habilitada, es posible agregar datos redirigidos al contenido existente. Esto se logra con una redirección escrita con dos símbolos mayores que >>
:
$ cat /proc/cpu_info 2>>/tmp/error.txt $ cat /tmp/error.txt cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
En el ejemplo anterior, el nuevo mensaje de error se agregó al existente en el archivo /tmp/error.txt
. Si el archivo aún no existe, se creará con los nuevos datos.
La fuente de datos de la entrada estándar de un proceso también se puede reasignar. El símbolo menor que <
se usa para redirigir el contenido de un archivo al stdin de un proceso. En este caso, los datos fluyen de derecha a izquierda: se supone que el descriptor reasignado es 0 a la izquierda del símbolo menor que y el origen de datos (una ruta a un archivo) debe estar a la derecha del símbolo menor. El comando uniq
, como la mayoría de las utilidades de línea de comandos para procesar texto, acepta los datos enviados a stdin por defecto:
$ uniq -c </tmp/error.txt 2 cat: /proc/cpu_info: No such file or directory
La opción -c
hace que uniq
muestre cuántas veces aparece una línea repetida en el texto. Como se suprimió el valor numérico del descriptor de archivo redirigido, el comando de ejemplo es equivalente a uniq -c 0</tmp/error.txt
. Usar un descriptor de archivo que no sea 0
en una redirección de entrada solo tiene sentido en contextos específicos, porque es posible que un programa solicite datos en los descriptores de archivo 3
, 4
, etc. De hecho, los programas pueden usar cualquier número entero por encima de 2 como nuevos descriptores de archivo para entrada/salida de datos. Por ejemplo, el siguiente código C lee los datos del descriptor de archivo 3
y simplemente lo replica en el descriptor de archivo 4
:
Note
|
El programa debe manejar dichos descriptores de archivo correctamente, de lo contrario podría intentar una operación de lectura o escritura no válida y bloquearse. |
#include <stdio.h> int main(int argc, char **argv){ FILE *fd_3, *fd_4; // Open file descriptor 3 fd_3 = fdopen(3, "r"); // Open file descriptor 4 fd_4 = fdopen(4, "w"); // Read from file descriptor 3 char buf[32]; while ( fgets(buf, 32, fd_3) != NULL ){ // Write to file descriptor 4 fprintf(fd_4, "%s", buf); } // Close both file descriptors fclose(fd_3); fclose(fd_4); }
Para probarlo, guarde el código de muestra como fd.c
y compílelo con gcc -o fd fd.c
. Este programa necesita que estén disponibles los descriptores de archivo 3 y 4 para poder leerlos y escribirlos. Como ejemplo, el archivo creado previamente /tmp/error.txt
se puede utilizar como fuente del descriptor de archivo 3
y el descriptor de archivo 4
se puede redirigir a stdout:
$ ./fd 3</tmp/error.txt 4>&1 cat: /proc/cpu_info: No such file or directory cat: /proc/cpu_info: No such file or directory
Desde la perspectiva del programador, el uso de descriptores de archivos evita tener que lidiar con el análisis de opciones y las rutas del sistema de archivos. El mismo descriptor de archivo puede incluso usarse como entrada y salida. En este caso, el descriptor de archivo se define en la línea de comandos con símbolos menores y mayores que, como en 3<>/tmp/error.txt
.
Here Document y Here String
Otra forma de redirigir la entrada involucra los métodos Here Document y Here String. La redirección de documentos Here permite escribir texto de varias líneas que se utilizará como contenido redirigido. Dos símbolos menor que <<
indican una redirección de Here Document:
$ wc -c <<EOF > How many characters > in this Here document? > EOF 43
A la derecha de los dos símbolos menor que <<
se encuentra el término final EOF
. El modo de inserción finalizará tan pronto como se ingrese una línea que contenga solo el término final. Se puede usar cualquier otro término como término final, pero es importante no poner caracteres en blanco entre el símbolo menor que y el término final. En el ejemplo anterior, las dos líneas de texto se enviaron al stdin del comando wc -c
, que muestra el recuento de caracteres. Al igual que con los redireccionamientos de entrada para archivos, se supone el stdin (descriptor de archivo 0
) si se suprime el descriptor de archivo redirigido.
El método Here String es muy similar al método de Here Document, pero solo para una línea:
$ wc -c <<<"How many characters in this Here string?" 41
En este ejemplo, la cadena a la derecha de los tres signos menor que se envía al stdin de wc -c
, que cuenta el número de caracteres. Las cadenas que contienen espacios deben estar entre comillas, de lo contrario, solo la primera palabra se usará como la cadena Here y las restantes se pasarán como argumentos al comando.
Ejercicios Guiados
-
Además de los archivos de texto, el comando
cat
también puede trabajar con datos binarios, como enviar el contenido de un dispositivo de bloque a un archivo. Usando la redirección, ¿cómo puedecat
enviar el contenido del dispositivo/dev/sdc
al archivosdc.img
en el directorio actual? -
¿Cuál es el nombre del canal estándar redirigido por el comando
date 1> now.txt
? -
Después de intentar sobrescribir un archivo usando la redirección, un usuario recibe un error informando que la opción
noclobber
está habilitada. ¿Cómo se puede desactivar la opciónnoclobber
para la sesión actual? -
¿Cuál será el resultado del comando
cat <<.>/dev/stdout
?
Ejercicios Exploratorios
-
El comando
cat /proc/cpu_info
muestra un mensaje de error porque/proc/cpu_info
no existe. El comandocat /proc/cpu_info 2>1
redirige el mensaje de error a dónde? -
¿Será posible descartar el contenido enviado a
/dev/null
si la opciónnoclobber
está habilitada para la sesión de shell actual? -
Sin usar
echo
, ¿cómo se podría redirigir el contenido de la variable$USER
al stdin del comandosha1sum
? -
El kernel de Linux mantiene enlaces simbólicos en
/proc/PID/fd/
a cada archivo abierto por un proceso, donde PID es el número de identificación del proceso correspondiente. ¿Cómo podría el administrador del sistema usar ese directorio para verificar la ubicación de los archivos de registro abiertos pornginx
, suponiendo que su PID sea1234
? -
Es posible hacer cálculos aritméticos utilizando solo comandos integrados de shell, pero los cálculos de coma flotante requieren programas específicos, como
bc
(basic calculator). Conbc
incluso es posible especificar el número de lugares decimales, con el parámetroscale
. Sin embargo,bc
acepta operaciones solo a través de su entrada estándar, generalmente ingresada en modo interactivo. Usando una cadena Here, ¿cómo puede la operación de coma flotantescale = 6; 1 / 3
enviarse a la entrada estándar debc
?
Resumen
Esta lección cubre los métodos para ejecutar un programa que redirige sus canales de comunicación estándar. Los procesos de Linux utilizan estos canales estándar como descriptores genéricos de archivos para leer y escribir datos, lo que permite cambiarlos arbitrariamente a archivos o dispositivos. La lección sigue los siguientes pasos:
-
Qué son los descriptores de archivos y el papel que juegan en Linux.
-
Los canales de comunicación estándar de cada proceso: stdin, stdout y stderr.
-
Cómo ejecutar correctamente un comando utilizando la redirección de datos, tanto para entrada como para salida.
-
Cómo usar Here Document y Here String en las redirecciones de entrada
Los comandos y procedimientos abordados fueron:
-
Operadores de redireccionamiento:
>
,<
,>>
,<<
,<<<
. -
Comandos
cat
,set
,uniq
ywc
.
Respuestas a los ejercicios guiados
-
Además de los archivos de texto, el comando
cat
también puede trabajar con datos binarios, como enviar el contenido de un dispositivo de bloque a un archivo. Usando la redirección, ¿cómo puedecat
enviar el contenido del dispositivo/dev/sdc
al archivosdc.img
en el directorio actual?$ cat /dev/sdc > sdc.img
-
¿Cuál es el nombre del canal estándar redirigido por el comando
date 1> now.txt
?Salida estándar o stdout
-
Después de intentar sobrescribir un archivo usando la redirección, un usuario recibe un error informando que la opción
noclobber
está habilitada. ¿Cómo se puede desactivar la opciónnoclobber
para la sesión actual?set +C
oset +o noclobber
-
¿Cuál será el resultado del comando
cat <<.>/dev/stdout
?Bash ingresará al modo de entrada Heredoc, luego saldrá cuando aparezca un punto en una línea por sí mismo. El texto escrito se redirigirá a stdout (impreso en la pantalla).
Respuestas a ejercicios exploratorios
-
El comando
cat /proc/cpu_info
muestra un mensaje de error porque/proc/cpu_info
no existe. El comandocat /proc/cpu_info 2>1
redirige el mensaje de error a dónde?A un archivo llamado
1
en el directorio actual. -
¿Será posible descartar el contenido enviado a
/dev/null
si la opciónnoclobber
está habilitada para la sesión de shell actual?Si.
/dev/null
es un archivo especial no afectado pornoclobber
. -
Sin usar
echo
, ¿cómo se podría redirigir el contenido de la variable$USER
al stdin del comandosha1sum
?$ sha1sum <<<$USER
-
El kernel de Linux mantiene enlaces simbólicos en
/proc/PID/fd/
a cada archivo abierto por un proceso, donde PID es el número de identificación del proceso correspondiente. ¿Cómo podría el administrador del sistema usar ese directorio para verificar la ubicación de los archivos de registro abiertos pornginx
, suponiendo que su PID sea1234
?Al emitir el comando
ls -l /proc/1234/fd
, que mostrará los objetivos de cada enlace simbólico en el directorio. -
Es posible hacer cálculos aritméticos utilizando solo comandos integrados de shell, pero los cálculos de coma flotante requieren programas específicos, como
bc
(basic calculator). Conbc
incluso es posible especificar el número de lugares decimales, con el parámetroscale
. Sin embargo,bc
acepta operaciones solo a través de su entrada estándar, generalmente ingresada en modo interactivo. Usando una cadena Here, ¿cómo puede la operación de coma flotantescale = 6; 1 / 3
se enviará a la entrada estándar debc
?$ bc <<<"scale=6; 1/3"