103.4 Lección 2
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: |
2 de 2 |
Introducción
Un aspecto de la filosofía de Unix
establece que cada programa debe tener un
propósito específico y no debe tratar de
incorporar características fuera de su
alcance. Pero mantener las cosas simples
no significa resultados menos elaborados,
ya que se pueden encadenar diferentes
programas para producir un resultado
combinado. El caracter de barra vertical |
,
también conocido como el símbolo pipe,
se puede usar para crear una tubería que
conecte la salida de un programa
directamente a la entrada de otro,
mientras que command substitution
permite almacenar la salida de un programa
en una variable o usarlo directamente como
argumento para otro comando.
Tuberías (Pipes)
A diferencia de los
redireccionamientos, con las tuberías
los datos fluyen de izquierda a derecha
en la línea de comandos y el objetivo es
otro proceso, no una ruta del sistema de
archivos, un descriptor de archivo o un
documento. El carácter de canalización |
le dice al shell que inicie todos los
comandos distintos al mismo tiempo y que
conecte la salida del comando anterior a
la entrada del siguiente comando, de
izquierda a derecha. Por ejemplo, en
lugar de utilizar redireccionamientos,
el contenido del archivo /proc/cpuinfo
enviado a la salida estándar por cat
puede canalizarse al stdin de wc
con el siguiente comando:
$ cat /proc/cpuinfo | wc 208 1184 6096
En ausencia de una ruta a un archivo, wc
cuenta el número de líneas, palabras y
caracteres que recibe en su stdin, como
es el caso en el ejemplo. Muchas
tuberías pueden estar presentes en un
comando compuesto. En el siguiente
ejemplo, se utilizan dos tuberías:
$ cat /proc/cpuinfo | grep 'model name' | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
El contenido del archivo /proc/cpuinfo
producido por cat /proc/cpuinfo
se canalizó al comando grep
'model name'
, que luego
selecciona solo las líneas que contienen
el término model name
. La
máquina que ejecuta el ejemplo tiene
muchas CPU, por lo que hay líneas
repetidas con model name
.
La última tubería conecta el nombre del
modelo grep 'model name'
a
uniq
, que es responsable de
omitir cualquier línea igual a la
anterior.
Las tuberías se pueden combinar con redireccionamientos en la misma línea de comando. El ejemplo anterior se puede reescribir en una forma más simple:
$ grep 'model name' </proc/cpuinfo | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
La redirección de entrada para grep
no es estrictamente necesaria ya que grep
acepta una ruta de archivo como
argumento, pero el ejemplo demuestra
cómo construir dichos comandos
combinados.
Las tuberías y redirecciones son
exclusivas, es decir, una fuente puede
asignarse a un solo destino. Sin
embargo, es posible redirigir una salida
a un archivo y aún verlo en la pantalla
con el programa tee
. Para
hacerlo, el primer programa envía su
salida al stdin de tee
y
se le proporciona un nombre de archivo a
este último para almacenar los datos:
$ grep 'model name' </proc/cpuinfo | uniq | tee cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz $ cat cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
El resultado del último programa de la
cadena, generado por uniq
,
se muestra y almacena en el archivo cpu_model.txt
.
Para no sobrescribir el contenido del
archivo proporcionado sino para
agregarle datos, la opción -a
debe proporcionarse a tee
.
Solo la salida estándar de un proceso es capturada por una tubería. Digamos que debe pasar por un largo proceso de compilación en la pantalla y al mismo tiempo guardar tanto la salida estándar como el error estándar en un archivo para su posterior inspección. Suponiendo que su directorio actual no tiene un Makefile, el siguiente comando generará un error:
$ make | tee log.txt make: *** No targets specified and no makefile found. Stop.
Aunque se muestra en la pantalla, el
mensaje de error generado por make
no fue capturado por tee
y
el archivo log.txt se creó
vacío. Se debe hacer una redirección
antes de que una tubería pueda capturar
el stderr:
$ make 2>&1 | tee log.txt make: *** No targets specified and no makefile found. Stop. $ cat log.txt make: *** No targets specified and no makefile found. Stop.
En este ejemplo, el stderr de make
se redirigió al stdout, por lo que tee
pudo capturarlo con una tubería,
mostrarlo en la pantalla y guardarlo en
el archivo log.txt
. En
casos como este, puede ser útil guardar
los mensajes de error para su posterior
inspección.
Sustitución de comando
Otro método para capturar la salida de un comando es command substitution (sustitución de comando). Al colocar un comando dentro de las comillas inversas, Bash lo reemplaza con su salida estándar. El siguiente ejemplo muestra cómo usar el stdout de un programa como argumento para otro programa:
$ mkdir `date +%Y-%m-%d` $ ls 2019-09-05
La salida del programa date
,
la fecha actual formateada como año-mes-día,
se utilizó como argumento para crear un
directorio con mkdir
. Se
obtiene un resultado idéntico usando $()
en lugar de comillas inversas:
$ rmdir 2019-09-05 $ mkdir $(date +%Y-%m-%d) $ ls 2019-09-05
El mismo método puede usarse para almacenar la salida de un comando como una variable:
$ OS=`uname -o` $ echo $OS GNU/Linux
El comando uname -o
genera el nombre genérico del sistema
operativo actual, que hemos almacenado
en la variable de sesión OS
.
Asignar la salida de un comando a una
variable es muy útil en los scripts, ya
que permite almacenar y evaluar los
datos de muchas maneras distintas.
Dependiendo de la salida generada por
el comando reemplazado, la sustitución
del comando incorporado puede no ser
apropiada. Un método más sofisticado
para usar la salida de un programa como
argumento de otro programa emplea un
intermediario llamado xargs
.
El programa xargs
usa los
contenidos que recibe a través de stdin
para ejecutar un comando dado con los
contenidos como argumento. El siguiente
ejemplo muestra xargs
ejecutando el programa identify
con argumentos proporcionados por el
programa find
:
$ find /usr/share/icons -name 'debian*' | xargs identify -format "%f: %wx%h\n" debian-swirl.svg: 48x48 debian-swirl.png: 22x22 debian-swirl.png: 32x32 debian-swirl.png: 256x256 debian-swirl.png: 48x48 debian-swirl.png: 16x16 debian-swirl.png: 24x24 debian-swirl.svg: 48x48
El programa identify
es
parte de ImageMagick, un
conjunto de herramientas de línea de
comandos para inspeccionar, convertir y
editar la mayoría de los tipos de
archivos de imagen. En el ejemplo, xargs
tomó todas las rutas listadas por find
y las puso como argumentos para identify
,
que luego muestra la información para
cada archivo formateado como lo requiere
la opción -format
. Los
archivos encontrados por find
en el ejemplo son imágenes que contienen
el logotipo de distribución en un
sistema de archivos Debian. -format
es un parámetro para identify
,
no para xargs
.
La opción -n 1
requiere
que xargs
ejecute el
comando dado con un solo argumento a la
vez. En el caso del ejemplo, en lugar de
pasar todas las rutas encontradas por find
como una lista de argumentos para identify
,
usar xargs -n 1
ejecutaría
el comando identify
para
cada ruta por separado. El uso de -n
2
ejecutaría identify
con dos rutas como argumentos, -n
3
con tres rutas como
argumentos y así sucesivamente. De
manera similar, cuando xargs
procesa contenidos de varias líneas,
como es el caso de la entrada
proporcionada por find
, la
opción -L
puede usarse
para limitar cuántas líneas se usarán
como argumentos por ejecución de
comando.
Note
|
Usar |
Si las rutas tienen caracteres de
espacio, es importante ejecutar find
con la opción -print0
.
Esta opción indica a find
que use un carácter nulo entre cada
entrada para que la lista pueda ser
analizada correctamente por xargs
(se suprimió la salida):
$ find . -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 du | sort -n
La opción -0
le dice a xargs
que el caracter nulo debe usarse como
separador. De esa manera, las rutas de
archivo proporcionadas por find
se analizan correctamente incluso si
tienen caracteres en blanco u otros
caracteres especiales. El ejemplo
anterior muestra cómo usar el comando du
para averiguar el espacio ocupado en
disco de cada archivo encontrado y luego
ordenar los resultados por tamaño. La
salida fue suprimida por concisión.
Tenga en cuenta que para cada criterio
de búsqueda es necesario colocar la
opción -print0
para find
.
Por defecto, xargs
coloca
los argumentos del comando ejecutado en
último lugar. Para cambiar ese
comportamiento, se debe usar la opción -I
:
$ find . -mindepth 2 -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 -I PATH mv PATH ./
En el último ejemplo, cada archivo
encontrado por find
se
mueve al directorio actual. Como las
rutas de origen deben ser informadas a mv
antes de la ruta de destino, se da un
término de sustitución a la opción -I
de xargs
que luego se
coloca apropiadamente junto a mv
.
Al utilizar el caracter nulo como
separador, no es necesario encerrar el
término de sustitución con comillas.
Ejercicios Guiados
-
Es conveniente guardar la fecha de ejecución de las acciones realizadas por scripts automatizados. El comando
date +%Y-%m-%d
muestra la fecha actual en formato año-mes-día. ¿Cómo se puede almacenar la salida de dicho comando en una variable de shell llamadaTODAY
usando la sustitución de comandos?
-
Usando el comando
echo
, ¿cómo se puede enviar el contenido de la variableTODAY
a la entrada estándar del comandosed s/-/./g
?
-
¿Cómo podría usarse la salida del comando
date +%Y-%m-%d
como una cadena Here para ordenarsed s/-/./g
?
-
El comando
convert image.jpeg -resize 25% small/image.jpeg
crea una versión más pequeña deimage.jpeg
y coloca la imagen resultante en un archivo con el mismo nombre dentro del subdirectoriosmall
. Usandoxargs
, ¿cómo es posible ejecutar el mismo comando para cada imagen listada en el archivofilelist.txt
?
Ejercicios Exploratorios
-
Una rutina de copia de seguridad simple crea periódicamente una imagen de partición
/dev/sda1
condd < /dev/sda1 > sda1.img
. Para realizar futuras comprobaciones de integridad de datos, la rutina también genera un hash SHA1 del archivo consha1sum < sda1.img > sda1.sha1
. Al agregar tuberías y el comandotee
, ¿cómo se combinarían estos dos comandos en uno?
-
El comando
tar
se usa para archivar muchos archivos en uno solo, preservando la estructura del directorio. La opción-T
permite especificar un archivo que contiene las rutas a archivar. Por ejemplo,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crea un archivo tar comprimidoetc.tar.xz
de la lista provista por el comandofind
(la opción-T-
indica la entrada estándar como la lista de ruta). Para evitar posibles errores de análisis debido a las rutas que contienen espacios, ¿qué opciones de comando deberían estar presentes parafind
ytar
?
-
En lugar de abrir una nueva sesión de shell remota, el comando
ssh
solo puede ejecutar un comando indicado como argumento:ssh user@storage "remote command"
. Dado quessh
también permite redirigir la salida estándar de un programa local a la entrada estándar del programa remoto, ¿cómo canalizaría el comandocat
un archivo local llamadoetc.tar.gz
a/srv/backup/etc.tar.gz
enuser@storage
a través dessh
?
Resumen
Esta lección cubre las técnicas tradicionales de comunicación entre procesos empleadas por Linux. La canalización de comandos crea un canal de comunicación unidireccional entre dos procesos y la sustitución de comandos permite almacenar la salida de un proceso en una variable de shell. La lección sigue los siguientes pasos:
-
Cómo se pueden usar pipes para transmitir la salida de un proceso a la entrada de otro.
-
El propósito de los comandos
tee
yxargs
. -
Cómo capturar el resultado de un proceso con command sustitution, almacenándolo en una variable o usándolo directamente como parámetro para otro comando.
Los comandos y procedimientos abordados fueron:
-
Comando de canalización con
|
. -
Sustitución de comandos con backticks y
$()
. -
Comandos
tee
,xargs
yfind
.
Respuestas a los ejercicios guiados
-
Es conveniente guardar la fecha de ejecución de las acciones realizadas por scripts automatizados. El comando
date +%Y-%m-%d
muestra la fecha actual en formato año-mes-día. ¿Cómo se puede almacenar la salida de dicho comando en una variable de shell llamadaTODAY
usando la sustitución de comandos?$ TODAY=`date +%Y-%m-%d`
o
$ TODAY=$(date +%Y-%m-%d)
-
Usando el comando
echo
, ¿cómo se puede enviar el contenido de la variableTODAY
a la entrada estándar del comandosed s/-/./g
?$ echo $TODAY | sed s/-/./g
-
¿Cómo podría usarse la salida del comando
date +%Y-%m-%d
como una cadena Here para ordenarsed s/-/./g
?$ sed s/-/./g <<< `date +%Y-%m-%d`
o
$ sed s/-/./g <<< $(date +%Y-%m-%d)
-
El comando
convert image.jpeg -resize 25% small/image.jpeg
crea una versión más pequeña deimage.jpeg
y coloca la imagen resultante en un archivo con el mismo nombre dentro del subdirectoriosmall
. Usandoxargs
, ¿cómo es posible ejecutar el mismo comando para cada imagen listada en el archivo` filelist.txt`?$ xargs -I IMG convert IMG -resize 25% small/IMG < filelist.txt
o
$ cat filelist.txt | xargs -I IMG convert IMG -resize 25% small/IMG
Respuestas a ejercicios exploratorios
-
Una rutina de copia de seguridad simple crea periódicamente una imagen de partición
/dev/sda1
condd < /dev/sda1 > sda1.img
. Para realizar futuras comprobaciones de integridad de datos, la rutina también genera un hash SHA1 del archivo consha1sum < sda1.img > sda1.sha1
. Al agregar tuberías y el comandotee
, ¿cómo se combinarían estos dos comandos en uno?# dd < /dev/sda1 | tee sda1.img | sha1sum > sda1.sha1
-
El comando
tar
se usa para archivar muchos archivos en uno solo, preservando la estructura del directorio. La opción-T
permite especificar un archivo que contiene las rutas a archivar. Por ejemplo,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crea un archivo tar comprimidoetc.tar.xz
de la lista provista por el comandofind
(la opción-T-
indica la entrada estándar como la lista de ruta). Para evitar posibles errores de análisis debido a las rutas que contienen espacios, ¿qué opciones de comando deberían estar presentes parafind
ytar
?Opciones
-print0
y--null
:$ find /etc -type f -print0 | tar -cJ -f /srv/backup/etc.tar.xz -T - --null
-
En lugar de abrir una nueva sesión de shell remota, el comando
ssh
solo puede ejecutar un comando indicado como argumento:ssh user@storage "remote command"
. Dado quessh
también permite redirigir la salida estándar de un programa local a la entrada estándar del programa remoto, ¿cómo canalizaría el comandocat
un archivo local llamadoetc.tar.gz
a/srv/backup/etc.tar.gz
enuser@storage
a través dessh
?$ cat etc.tar.xz | ssh user@storage "cat > /srv/backup/etc.tar.xz"
o
$ ssh user@storage "cat > /srv/backup/etc.tar.xz" < etc.tar.xz