Semana 9 - Standard IO and Pipes

También podría gustarte

Está en la página 1de 30

1

Standard IO and Pipe

Capítulo 1 Entrada estándar y salida estándar

Conceptos clave

• Los programas basados en la terminal tienden a leer información desde una


fuente y a escribir la información en un destino.
• La fuente desde donde se leen los programas se conoce como una entrada
estándar (stdin, del inglés standard in) y suele estar conectada al teclado de la
terminal.
• El destino al que los programas escriben se conoce como una salida estándar
(stdout, del inglés standard out) y suele estar conectada a la pantalla de la
terminal.
• Cuando se utiliza la shell bash, la stdout puede redirigirse mediante > o >> y la
stdin puede redirigirse mediante <.

Muchos comandos de Linux leen la entrada desde el teclado y muestran la salida en la


terminal. En este cuaderno, aprenderá cómo se puede redirigir desde dónde se lee la
entrada y a dónde va la salida. La salida de un comando puede utilizarse como la
entrada para otro comando, permitiendo que los comandos sencillos se utilicen
conjuntamente para realizar tareas más complejas.

Tres tipos de programas

En Linux (y Unix), los programas se pueden agrupar en los siguientes tres diseños.

Programas gráficos

Los programas gráficos están diseñados para ejecutarse en el entorno gráfico X.


Esperan que el usuario esté utilizando el ratón y los componentes gráficos
comunes tales como menús emergentes y botones para la entrada de parte del
usuario. El navegador de red mozilla es un ejemplo de un programa gráfico.

Programas de pantalla

Los programas de pantalla esperan utilizar una consola de texto. Hacen uso de
toda la pantalla y manejan la presentación del texto y rediseño de pantalla en
formas sofisticadas. No necesitan ratón y son apropiados para terminales y
consolas virtuales. Los editores de texto vi, nano y el navegador de red links
son ejemplos de este tipo de programas.

Programas de terminal

Los programas de terminal reunen entradas y salidas de pantalla en un flujo,


raras veces rediseña la pantalla como si escribiese directamente a la impresora lo
cual no permite al cursor devolver la página. Debido a su sencillez, los
programas basados en la terminal suelen llamarse simplemente comandos.
Ejemplos de este tipo de programas son ls, grep y useradd.
2

Standard IO and Pipe

Este capítulo se enfoca en este último tipo de programa. No permita que la simplicidad
de estos comandos que reciben entradas y salidas lo engañen. Usted hallará que muchos
de estos comandos son muy sofisticados y le permiten utilizar la interfaz de la línea de
comandos de manera eficaz.

Entrada estándar (stdin) y salida estándar (stdout)

Los programas de terminal suelen leer información como un flujo desde una sola fuente
tal como el teclado de una terminal. Igualmente, por lo general, escriben información
como un flujo a un solo destino como por ejemplo una pantalla. En Linux (y Unix), el
flujo de entrada se conoce como entrada estándar (suele abreviarse stdin) y el flujo de
salida se conoce como salida estándar (o en forma abreviada stdout).

Por lo general, stdin y stdout están conectadas a la terminal que ejecuta el comando.
Algunas veces para automatizar los comandos más repetidos, grabar la salida de un
comando o incluirlo más tarde en un informe o correo se considera conveniente
redirigir stdin desde stdout hacia los archivos.

Redirección de stdout

Escritura de una salida a un archivo

Cuando un programa de terminal genera salida, usualmente suele escribir esa salida a su
flujo de stdout, sin saber qué está conectado al final receptor de ese flujo. Con
frecuencia el flujo de stdout está conectado a la terminal que inició el proceso para que
la salida sea escrita a la pantalla de la terminal. La shell bash usa > para redirigir un
flujo de stdout de proceso a un archivo.

Por ejemplo, suponga que la máquina que elvis está utilizando se vuelve muy lenta y no
responde. Con el fin de diagnosticar el problema, elvis desearía examinar los procesos
que están ejecutándose. Sin embargo, dado que la máquina es tan lenta, recoge
información ahora pero la analiza más tarde. Elvis puede redirigir la salida del comando
ps aux al archivo sluggish.txt y regresar para examinar el archivo cuando la máquina
esté respondiendo mejor.

[elvis@station elvis]$ ps aux > sluggish.txt


[elvis@station elvis]$

Observe que no se ve ninguna salida en la terminal. El comando ps escribe en stdout,


como siempre lo hace, pero stdout es redirigida por la shell bash al archivo
sluggish.txt. El usuario elvis puede examinar el archivo más tarde en un momento
más apropiado.

[elvis@station elvis]$ head sluggish.txt


USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1380 76 ? S Jun02 0:04 init [
root 2 0.0 0.0 0 0 ? SW Jun02 0:00
[keventd]
root 3 0.0 0.0 0 0 ? SW Jun02 0:00 [kapmd]
3

Standard IO and Pipe

root 4 0.0 0.0 0 0 ? SWN Jun02 0:00


[ksoftirqd_CPU0]
root 9 0.0 0.0 0 0 ? SW Jun02 0:00
[bdflush]
root 5 0.0 0.0 0 0 ? SW Jun02 0:00
[kswapd]
root 6 0.0 0.0 0 0 ? SW Jun02 0:00
[kscand/DMA]
root 7 0.0 0.0 0 0 ? SW Jun02 0:37
[kscand/Normal]
root 8 0.0 0.0 0 0 ? SW Jun02 0:00
[kscand/HighMem]

Agregando una salida a un archivo

Si el archivo sluggish.txt ya existió, su contenido original se perdería. Esto suele


conocerse como sobrescribir un archivo. Para agregar una salida de un comando a un
archivo, en lugar de sobrescribirlo, bash usa >>.

Suponga que elvis quiso registrar un marcador de tiempo de cuando se estaba


presentando la conducta lenta, como también una lista de los procesos actuales en
ejecución. Primero podría crear (o sobrescribir ) el archivo con la salida del
comandodate mediante > y luego agregarlo a la salida del comando ps aux mediante
>>.

[elvis@station elvis]$ date > sluggish.txt


[elvis@station elvis]$ ps aux >> sluggish.txt
[elvis@station elvis]$ head sluggish.txt
Tue Jun 3 16:57:23 EDT 2003
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1380 76 ? S Jun02 0:04 init [
root 2 0.0 0.0 0 0 ? SW Jun02 0:00
[keventd]
root 3 0.0 0.0 0 0 ? SW Jun02 0:00 [kapmd]
root 4 0.0 0.0 0 0 ? SWN Jun02 0:00
[ksoftirqd_CPU0]
root 9 0.0 0.0 0 0 ? SW Jun02 0:00
[bdflush]
root 5 0.0 0.0 0 0 ? SW Jun02 0:00
[kswapd]
root 6 0.0 0.0 0 0 ? SW Jun02 0:00
[kscand/DMA]
root 7 0.0 0.0 0 0 ? SW Jun02 0:37
[kscand/Normal]

Redirección de stdin

Así como bash usa > para lograr que los comandos entreguen su salida en alguna otra
parte que no sea la pantalla, bash usa < para hacer que lean entradas desde alguna parte
diferente al teclado. El usuario elvis todavía está tratando de entender el porqué su
máquina está lenta. Habla con su administrador local de sistemas , quien piensa que
examinar la lista de los procesos en ejecución es una buena idea y le pide a elvis que le
envíe una copia por correo.
4

Standard IO and Pipe

Por medio del comando mail basado en la terminal, elvis escribe "manualmente" desde
el teclado un correo electrónico al administrador. El comando mail espera un
destinatario como argumento y la línea de asunto se puede especificar con la opción -s.
El cuerpo del texto del correo electronico se introduce luego desde el teclado. El final
del texto se señala con un punto aparte en una línea.

[elvis@station elvis]$ mail -s "Computer is sluggish"


sysadmin@example.com

Hey sysadmin...

I'm sending a list of processes that were running when the computer
was running
in a separate email.

Thanks! --elvis
.

Cc:

Para su mensaje de seguimiento, elvis puede fácilmente enviar la salida del comando ps
grabada en el archivo sluggish.txt. Sólo redirige el flujo de stdin del comando mail
para leerlo desde el archivo.

[elvis@station elvis]$ mail -s "ps output" sysadmin@example.com <


sluggish.txt

El administrador de sistemas recibirá un correo electrónico de elvis con "salida ps"


como su línea de asunto y el contenido del archivo sluggish.txt como el cuerpo del
texto.

En el primer caso, la stdin del proceso mail estaba conectada a la terminal y el cuerpo
del mensaje lo proporcionó el teclado. En el segundo caso, bash arregló para que la
stdin del proceso mail se conectara al archivo sluggish.txt y el cuerpo del mensaje
fuera provisto por su contenido. El comando mail no cambia su conducta básica. Este
lee el cuerpo del mensaje desde stdin. [1]

Dentro del cofre: archivos abiertos y descriptores de archivos

Archivos abiertos y descriptores de archivos

Para apreciar plenamente cómo administrar procesos de entrada y salida estándar y


archivos debemos introducir el concepto de un descriptor de archivos. Con el fin de leer
o escribir información en un archivo un proceso debe abrir el archivo. Los procesos de
Linux (y Unix) mantienen el registro de los archivos que están abiertos mediante la
asignación de un número entero a cada uno. El número entero se conoce como un
descriptor de archivos.

El kernel de Linux ofrece una forma fácil de examinar los archivos abiertos y los
descriptores de archivos de un proceso en ejecución mediante el sistema de archivos
5

Standard IO and Pipe

/proc. Cada proceso tiene un subdirectorio asociado bajo /proc llamado como su PID
(ID del proceso). El subdirectorio del proceso a su vez tiene un subdirectorio llamado
fd (del inglésfile descriptor). Dentro del subdirectorio /proc/pid/fd, existe un enlace
simbólico para cada archivo abierto por el proceso. El nombre del enlace simbólico es el
número entero del descriptor de archivo abierto y el enlace simbólico apunta al archivo
mismo.

A continuación, elvis ejecuta con cat el archivo /etc/termcap y luego casi de


inmediato suspende el programa con CONTROL-Z.

[elvis@station elvis]$ cat /etc/termcap

[1]+ Stopped cat /etc/termcap

Usando el comando ps busca el PID del proceso, luego elvis examina el directorio del
proceso /proc/pid/fd.

[elvis@station elvis]$ ps
PID TTY TIME CMD
1368 pts/1 00:00:00 bash
1910 pts/1 00:00:00 cat
1911 pts/1 00:00:00 ps
[elvis@station elvis]$ ls -l /proc/1910/fd
total 0
lrwx------ 1 elvis elvis 64 Sep 13 06:42 0 -> /dev/tty1
lrwx------ 1 elvis elvis 64 Sep 13 06:42 1 -> /dev/tty1
lrwx------ 1 elvis elvis 64 Sep 13 06:42 2 -> /dev/tty1
lr-x------ 1 elvis elvis 64 Sep 13 06:42 3 ->
/etc/termcap
elvis observa que el PID del proceso cat es 1910.
elvis ahora mira en el subdirectorio, el cual corresponde al PID observado.

No es de sorprender que el proceso cat tenga abierto el archivo /etc/termcap (debe


poder leer el archivo para mostrar su contenido). Quizás un poco extraño es que éste no
esté sólo o incluso que no sea el primer archivo abierto por el proceso. El comando cat
tiene tres archivos abiertos antes que éste o más exactamente, el mismo archivo abierto
tres veces: /dev/tty1.

Como protocolo de Linux (y Unix), cada proceso hereda tres archivos abiertos tras el
inicio. El primero, el descriptor de archivo 0, es la entrada estándar. El segundo, el
archivo descriptor 1, es la salida estándar, y el tercero, el archivo descriptor 2, es el error
estándar (será tratado en la siguiente lección). ¿Qué archivos abiertos heredó el
comando cat de la shell bash que lo inició? El nodo del dispositivo /dev/tty1 para
todos los tres.

Table 1. Entrada estándar, salida estándar y descriptores de archivos de error


estándar

Flujo Descriptor Abbreviation


6

Standard IO and Pipe

Flujo Descriptor Abbreviation


Entrada estándar 0 stdin
Salida estándar 1 stdout
Error estándar 2 stderr

Recuerde que /dev/tty1 es el nodo del dispositivo conectado al controlador dentro del
kernel. Cualquier cosa que elvis teclee se puede leer desde este archivo y cualquier cosa
que se escriba en este archivo aparecerá en la terminal de elvis. ¿Qué sucede si el
proceso cat lee desde stdin? Éste lee la entrada desde el teclado de elvis. ¿Qué sucede si
éste escribe a stdout? Cualquier cosa que se escriba se verá en la terminal de elvis.

Redirección

En el siguiente ejemplo, elvis ejecuta con cat el archivo /etc/termcap pero esta vez
redirige stdout al archivo /tmp/foo. Una vez más, elvis suspende el comando en la
mitad del camino con las teclas CONTROL-Z.

[elvis@station elvis]$ cat /etc/termcap > /tmp/foo

[1]+ Stopped cat /etc/termcap >/tmp/foo

Utilizando la misma técnica anterior, elvis examina los archivos que el comando cat ha
abierto y los descriptores de archivo asociados con ellos.

[elvis@station elvis]$ ps
PID TTY TIME CMD
1368 pts/1 00:00:00 bash
1976 pts/1 00:00:00 cat
1977 pts/1 00:00:00 ps
[elvis@station elvis]$ ls -l /proc/1976/fd
total 0
lrwx------ 1 elvis elvis 64 Sep 13 07:05 0 ->
/dev/pts/1
l-wx------ 1 elvis elvis 64 Sep 13 07:05 1 -> /tmp/foo

lrwx------ 1 elvis elvis 64 Sep 13 07:05 2 ->


/dev/pts/1
lr-x------ 1 elvis elvis 64 Sep 13 07:05 3 ->
/etc/termcap
Observe que el descriptor de archivo 1 (en otras palabras, la salida estándar) no está
conectado a la terminal sino al archivo /tmp/foo.

¿Qué sucede cuando elvis redirige tanto la entrada como la salida estándar?

[elvis@station elvis]$ cat < /etc/termcap > /tmp/foo

[1]+ Stopped cat </etc/termcap >/tmp/foo


[elvis@station elvis]$ ps
PID TTY TIME CMD
1368 pts/1 00:00:00 bash
7

Standard IO and Pipe

1980 pts/1 00:00:00 cat


1988 pts/1 00:00:00 ps
[elvis@station elvis]$ ls -l /proc/1980/fd
total 0
lr-x------ 1 elvis elvis 64 Sep 13 07:07 0 ->
/etc/termcap
l-wx------ 1 elvis elvis 64 Sep 13 07:07 1 -> /tmp/foo
lrwx------ 1 elvis elvis 64 Sep 13 07:07 2 ->
/dev/pts/1
El descriptor de archivo 0 (entrada estándar) no está conectado a la terminal sino al
archivo /etc/termcap.

Cuando el comando cat se llama sin argumentos (por ejemplo, sin ningún nombre de
archivo o archivos para mostrar), éste muestra la entrada estándar en su lugar. En lugar
de abrir un archivo específico (mediante el descriptor de archivo 3, como el anterior), el
comando cat lee desde stdin.

¿Cuál es la diferencia de eficiencia entre los siguientes tres comandos?

[elvis@station elvis]$ cat < /etc/termcap > /tmp/foo


[elvis@station elvis]$ cat /etc/termcap > /tmp/foo
[elvis@station elvis]$ cp /etc/termcap /tmp/foo

No hay ninguna. Con el fin de apreciar el beneficio real del diseño de comandos para
leer desde la entrada estándar en lugar de los archivos llamados debemos esperar hasta
que veamos las tuberías en una próxima lección.

Ejemplos

Los siguientes ejemplos incluyen un ejemplo rápido de cómo los nuevos usuarios suelen
confundirse con comandos que leen desde la entrada estándar y un par de ejemplos de
"la vida real" que los programas ftp y gnuplot utilizan. Los programas ftp y gnuplot
son complicados y estos ejemplos apenas introducen algunas de sus funciones que
sirven para hacer énfasis en uno de los temas más importantes en este cuaderno: si el
programa es conducido desde una interfaz de línea de comandos, puede automatizarse
con un script sencillo de texto y redirección.

Salida del comando sort

A continuación, blondie emplea el comando sort para ordenar los animales que se
encuentran en el archivo de texto zoo.

[blondie@station blondie]$ cat zoo


elephant
seal
ape
giraffe
fish
[blondie@station blondie]$ sort zoo
ape
8

Standard IO and Pipe

elephant
fish
giraffe
seal

Como el nombre en inglés lo indica, el comando sort (en su forma más simple) lee un
archivo y escribe línea por línea en orden alfabético. Al igual que el comando cat,
cuando el comando sort se ejecuta sin argumentos (por ejemplo, nombres de archivo
para ordenar), esperará entradas desde stdin.

[blondie@station blondie]$ sort < zoo


ape
elephant
fish
giraffe
seal

Aunque esta conducta parece (y es) perfectamente razonable, a menudo confunde a los
nuevos usuarios quienes inocentemente teclean un nombre de comando, "sólo para ver
qué hace". A continuación, asuma que blondie no sabe aún sobre la entrada estándar. Al
explorar, invoca el comando sort. Sin entender que el comando sort está esperando para
leer la entrada estándar, por ejemplo, su teclado, trata de alguna manera de salir del
comando que ha iniciado. Por último, un amigo le dice en voz baja "CONTROL-D".

[blondie@station blondie]$ sort


ls
quit
man sort
exit
get me out of this
CTRL-D
exit
get me out of this
ls
man sort
quit
[blondie@station blondie]$

Tras teclear CONTROL-D, la secuencia de control convencional "Fin del archivo"


(recuerde el cuaderno 1), el comando sort imprime una lista ordenada de todo lo que se
lee en la entrada estándar.

Transferencias automáticas FTP

La usuaria blondie usualmente toma un archivo README desde el servidor ftp para el
proyecto del kernel de Linux, ftp.kernel.org. El servidor kernel.org ftp permite usuarios
anónimos, es decir, usuarios que entran con el nombre de usuario "anónimo". Cuándo se
les pide una contraseña, los usuarios anónimos de ftp no necesitan entregar ninguna,
pero por protocolo dan su dirección de correo electrónico en su lugar.

[blondie@station student]$ ftp ftp.kernel.org


Connected to ftp.kernel.org (204.152.189.116).
9

Standard IO and Pipe

220 ProFTPD [ftp.kernel.org]


Name (ftp.kernel.org:blondie): anonymous
331 Anonymous login ok, send your complete email address as your
password.
Contraseña: (blondie teclea su dirección de correo-electrónico)
230 Anonymous access granted, restrictions apply.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
227 Entering Passive Mode (204,152,189,116,237,224).
150 Opening ASCII mode data connection for file list
drwxr-s--- 2 korg mirrors 4096 May 21 2001
for_mirrors_only
drwx------ 2 root root 16384 Mar 18 00:27 lost+found
drwxrwsr-x 8 korg korg 4096 Mar 24 17:46 pub
226 Transfer complete.
ftp> cd pub
250 CWD command successful.
ftp> ls
227 Entering Passive Mode (204,152,189,116,237,229).
g150 Opening ASCII mode data connection for file list
drwxrws--- 2 korg korg 4096 Mar 18 04:05 RCS
-r--r--r-- 1 korg korg 1963 Oct 4 2001 README
-r--r--r-- 1 korg korg 578 Mar 18 04:04
README_ABOUT_BZ2_FILES
drwxrwsr-x 4 korg korg 4096 Oct 26 2000 dist
...
226 Transfer complete.
ftp> get README
local: README remote: README
227 Entering Passive Mode (204,152,189,116,237,237).
150 Opening BINARY mode data connection for README (1963 bytes).
226 Transfer complete.
1963 bytes received in 0.000564 secs (3.4e+03 Kbytes/sec)
ftp> quit
221 Goodbye.

Cuando el comando ftp hace una pausa con el intérprete de comandosftp>, blondie
teclea comandos para navegar los directorios del servidor ftp. Si blondie descargara este
archivo a menudo, podría sentirse tentada a escribir un archivo de texto sencillo,
getreadme.ftp, el cual reproduciría los comandos tecleados por ella. Podría entonces
ejecutar el mismo comando ftp ftp.kernel.org. Sin embargo, esta vez usaría < para
hacer que bash redirija stdin desde el archivo getreadme.ftp. Cuando el comando ftp
lee la entrada desde su flujo de stdin, la información es provista por el archivo en lugar
del teclado.

Primero, blondie utiliza un editor de texto sencillo para crear el archivo


getreadme.ftp, el cual contiene todos los comandos que ella escribió de modo
interactivo en el teclado (incluyendo la contraseña que dio al servidor anónimo ftp,
blondie@example.com).

[blondie@station blondie]$ cat getreadme.ftp


anonymous
blondie@example.com
10

Standard IO and Pipe

ls
cd pub
ls
get README
quit

Observe cómo el contenido del archivo coincide exactamente con lo que tecleó al
utilizar el comando anterior ftp. Luego, vuelve a ejecutar ftp ftp.kernel.org pero
redirige stdin desde el archivo recién creado.

[blondie@station blondie]$ ftp ftp.kernel.org < getreadme.ftp


Password:Name (ftp.kernel.org:blondie):
?Invalid command
drwxr-s--- 2 korg mirrors 4096 May 21 2001
for_mirrors_only
drwx------ 2 root root 16384 Mar 18 00:27 lost+found
drwxrwsr-x 8 korg korg 4096 Mar 24 17:46 pub
drwxrws--- 2 korg korg 4096 Mar 18 04:05 RCS
-r--r--r-- 1 korg korg 1963 Oct 4 2001 README
-r--r--r-- 1 korg korg 578 Mar 18 04:04
README_ABOUT_BZ2_FILES
drwxrwsr-x 4 korg korg 4096 Oct 26 2000 dist
-r--r--r-- 1 korg korg 1507 Oct 11 2001 index.html
drwxrwsr-x 8 korg korg 4096 Jan 21 2002 linux
drwxrwsr-x 3 korg korg 4096 Mar 24 17:46 scm
drwxrwsr-x 3 korg korg 4096 Oct 11 2001 site
drwxrwsr-x 11 korg korg 4096 Jan 1 2002 software
[blondie@station blondie]$ ls -l README
-rw-rw-r-- 1 blondie blondie 1963 Jun 3 17:37 README

Después de ejecutar el comando, blondie tiene un nuevo archivo README en su


directorio, el cual fue descargado por el comando ftp. Sin embargo, blondie tuvo un par
de contratiempos.

• Primero, el comando se detuvo y ella tuvo pulsar una vez ENTER para que
continuara. Por razones de seguridad, muchos comandos, cuando leen
contraseñas, no las leen desde stdin, sino desde la terminal directamente, (los
comandos no tienen que depender de stdin como su único medio de entrada,
aunque la mayoría lo hacen). Cuando ftp intentó leer la contraseña desde la
terminal, el programa se suspendió hasta que blondie pulsó la tecla ENTER.
• Segundo, hay una línea rara que dice ?Invalid Input. Dado que la contraseña
fue leída directamente desde la terminal, no se infirió desde el archivo
getreadme.ftp. Cuando el comando ftp fue a leer la siguiente línea de la
entrada decía blondie@example.com, lo que no reconoció como un comando.
• Por último, los listados de directorio fueron descargados en la terminal al
ejecutar el comando. Cuando el comando ftp ejecutó los comandos ls desde
getreadme.txt, escribió la salida en stdout, la cual está aún conectada a la
terminal. Debido a que blondie sabe dónde esta localizado el archivo y ha
incluido esa información dentro de un script, no necesita ver esos listados cada
vez que ejecuta el comando.
11

Standard IO and Pipe

Para resolver estos problemas, primero hace uso de un archivo~/.netrc. El comando


ftp está diseñado para buscar un archivo como ese en el directorio de inicio del usuario
y si existe, éste provee el nombre de usuario del usuario y la contraseña. Después de
estudiar la página del manual netrc(5), blondie usa un editor de texto sencillo para crear
el siguiente archivo ~/.netrc.

[blondie@station blondie]$ cat .netrc


default login anonymous password blondie@example.com

Dado que el archivo ~/.netrc ahora provee su nombre de usuario y contraseña, blondie
los suprime de su script getreadme.ftp. Luego, quita los comandos innecesarios ls
también desde el script.

[blondie@station blondie]$ cat getreadme.ftp


cd pub
get README
quit

Armada con su archivo ~/.netrc (para proveer su nombre de usuario y contraseña) y


su archivo getreadme.txt modificado (para proveer los comandos del programa ftp),
vuelve a ejecutar el comando ftp y la operación se realiza sin problemas.

[blondie@station blondie]$ head .netrc getreadme.ftp


==> .netrc <==
default login anonymous password user@site

==> getreadme.ftp <==


cd pub
get README
quit
[blondie@station blondie]$ ls
getreadme.ftp
[blondie@station blondie]$ ftp ftp.kernel.org < getreadme.ftp
[blondie@station blondie]$ ls
getreadme.ftp README

Generación automática de gráficos con gnuplot

La usuaria madonna desearía generar fácilmente diagramas de la actividad de la CPU de


su máquina. Está familiarizada con el comando vmstat, el cual hace muestreos y
cuadros de varios parámetros relacionados con el funcionamiento del sistema. El
comando puede tomar dos argumentos numéricos, el primero, especifica el periodo de
muestreo en segundos y el último, el número de muestras para recopilar.

Ella está interesada en las tres últimas columnas, las cuales son el porcentaje de tiempo
que está gastando la CPU en el sistema ("sy"), del usuario ("us") y en estado inactivo
("id"). Recopila 60 segundos de datos desde su máquina, la cual realiza muestreos cada
segundo.

[madonna@station madonna]$ vmstat 1 60 > stats.txt


[madonna@station madonna]$ head stats.txt
12

Standard IO and Pipe

procs memory swap io system


cpu
r b swpd free buff cache si so bi bo in cs us
sy id wa
2 6 0 17348 65604 277768 0 0 15 16 126 221 1
0 97 1
1 5 0 15736 66008 277788 0 0 376 6269 314 725 5
2 0 93
1 6 0 11496 67224 277392 0 0 1216 8 422 1533 15
16 0 69
0 6 0 10492 67944 277676 0 0 940 28 338 1193 7
8 0 85
0 6 0 10168 68324 277644 0 0 576 0 261 992 6
1 0 93
3 3 0 8848 69424 277864 0 0 1252 64 429 1386 10
16 0 74
3 3 0 8056 70188 277892 0 0 1068 1148 422 1215 8
16 0 76
1 6 0 12248 71084 277636 0 0 940 28 341 1275 9
4 0 87

Un poco frustrada porque las dos líneas de los encabezados interferirán con la
diagramación de los datos, madonna abre el archivo stats.txt en un editor de texto y
los borra con facilidad.

Para diagramar los datos, utiliza gnuplot, un sofisticado paquete de diagramación, el


cual usa comandos leídos desde una interfaz de terminal para generar diagramas de
funciones matemáticas y datos numéricos. Después de navegar un poco a través de la
ayuda en línea disponible dentro de gnuplot desarrolla los siguientes comandos para
diagramar sus datos como un archivo de gráficos PNG llamado cpu.png.

[madonna@station madonna]$ gnuplot

G N U P L O T
Version 3.7 patchlevel 3

...

Terminal type set to 'x11'


gnuplot> set term png
Terminal type set to 'png'
Options are ' small color'
gnuplot> set output 'cpu.png'
gnuplot> plot 'stats.txt' using 0:13 title "user" with lines,
'stats.txt' using
0:14 title "system" with lines, 'stats.txt' using 0:15 title "idle"
with lines
gnuplot> quit

Después de salir de gnuplot vuelve a la shell bash, donde utiliza el visor de pantalla
eog para ver su diagrama.

[madonna@station madonna]$ eog cpu.png


13

Standard IO and Pipe

Figure 1. Gráfico de madonna de la actividad de la CPU

Dado que madonna desearía a menudo generar un diagrama semejante, y no tener la


angustia de teclear el comando gnuplot a cada instante, genera un script que se puede
utilizar para automatizar gnuplot. Mediante un editor de texto, crea el archivo
cpu_plot.gnuplot, el cual contiene todos comandos gnuplot que entró desde el
teclado, teniendo el cuidado de poner un comando por línea.

[madonna@station madonna]$ cat cpu_plot.gnuplot


set term png
set output 'cpu.png'
plot 'stats.txt' using 0:13 title "user" with lines, 'stats.txt' using
0:14 titl
e "system" with lines, 'stats.txt' using 0:15 title "idle" with lines

Ahora puede diagramar con facilidad datos recopilados recientemente redirigiendo su


script como la stadin de gnuplot.

[madonna@station madonna]$ gnuplot < cpu_plot.gnuplot

Ejercicios en línea

Uso de la entrada estándar y la salida estándar

Lab Exercise
Objetivo: Usar la redirección de la shell bash para controlar de modo efectivo la
14

Standard IO and Pipe

entrada estándar y la salida estándar.

Tiempo estimado: 20 minutos.

Especificaciones

1. El comando hostname informa su nombre de máquina asignado de la estación


actual. Ejecute el comando (sin argumentos) y redirige la salida al archivo
~/stdoutlab.txt.
2. El comando uptime reporta cuánto tiempo ha pasado desde que arrancó su
máquina y otra información de uso del sistema. Ejecute el comando uptime (sin
argumentos) usando la redirección para agregar la salida al archivo
~/stdoutlab.txt.
3. El comando uname -a lista información sobre su versión actual de kernel.
Ejecute el comando usando la redirección para agregar la salida al archivo
~/stdoutlab.txt.

Si ha completado con éxito los tres pasos anteriores, usted debería poder
reproducir una salida semejante a la siguiente, (no se preocupe si la información
difiere de la que se muestra a continuación).

[student@station student]$ cat stdoutlab.txt


station
07:09:31 up 11:30, 5 users, load average: 0.19, 0.06, 0.01
Linux station 2.4.20-20.9 #1 Mon Aug 18 11:45:58 EDT 2003 i686
i686 i386 GNU/Linux

4. Genere un archivo de texto sencillo, ~/script.gnuplot, el cual sirva de script


para controlar gnuplot. Cuando gnuplot lea su script desde stdin, debería
generar un diagrama de una expresión matemática simple tal como el seno de x
(sin(x)) o x al cuadrado (x**2). El diagrama debería generarse como un gráfico
PNG llamado "gnuplot.png".

Una vez completado su script debería poderse utilizar como en el próximo


ejemplo.

[student@station student]$ ls
script.gnuplot
[student@station student]$ gnuplot < script.gnuplot
[student@station student]$ ls
gnuplot.png script.gnuplot
[student@station student]$ file gnuplot.png
gnuplot.png: PNG image data, 640 x 480, 8-bit colormap, non-
interlaced
[student@station student]$ eog gnuplot.png
15

Standard IO and Pipe

Deliverables

Question 1

1. Un archivo llamado ~/stdoutlab.txt, el cual contiene la salida del comando


hostname, seguido por la salida del comando uptime, seguido por la salida del
comando uname -a.
2. Un script ~/script.gnuplot, que cuando se utliliza como stdin para el
comando gnuplot, genera un archivo de gráficos PNG titulado gnuplot.png
conteniendo un diagrama de una función matemática simple.

Estrategia sugerida para la automatización de un gnuplot

Utilizando el ejemplo 3 como su guía, experimente de modo interactivo con gnuplot,


hasta que pueda generar un diagrama sencillo. Si está utilizando una terminal de texto
de un entorno gráfico X puede generar diagramas de texto al establecer su terminal de
salida gnuplot como terminal "tonta":

gnuplot> set term dumb

Una vez que pueda producir gráficos, establezca su tipo de terminal a png (para gráficos
PNG) y su archivo de salida en "gnuplot.png" mediante los siguientes comandos ...

gnuplot> set term png


gnuplot> set output "gnuplot.png"

... y genere su diagrama una vez más. Cuando haya entendido la secuencia de comandos
para generar un diagrama como un archivo PNG registre los comandos como su script
gnuplot.

Capítulo 2 Error estándar

Conceptos clave

• Los programas Unix reportan condiciones de error a un destino llamado error


estándar (stderr).
• Usualmente, stderr está conectado a una pantalla de terminal y los mensajes de
error se encuentran entremezclados con las salidas estándar.
• Cuando se utiliza la shell bash, el flujo de stderr puede redirigirse a un archivo
mediante 2>.
• Al utilizar bash, el flujo de stderr puede combinarse con el flujo de stdout
mediante 2>&1 o >&

Error estándar (stderr)


16

Standard IO and Pipe

Hemos discutido los flujos de salida y de entrada estándar, stdin y stdout y cómo usar >
y < en la línea de comandosbash para redirigirlos. Ahora estamos listos para complicar
un poco las cosas introduciendo un segundo flujo de salida, muy usado para reportar
condiciones de error, llamado error estándar (a menudo abreviado stderr).

En la siguiente secuencia, elvis está utilizando el comando head -1 para generar una
lista de las primeras líneas de todos los archivos en el directorio /etc/rc.d.

[elvis@station elvis]$ ls -F /etc/rc.d/


init.d/ rc0.d/ rc2.d/ rc4.d/ rc6.d/ rc.sysinit*
rc* rc1.d/ rc3.d/ rc5.d/ rc.local* rc.sysinit.rpmsave*
[elvis@station elvis]$ head -1 /etc/rc.d/*
==> /etc/rc.d/init.d <==
head: /etc/rc.d/init.d: Is a directory

==> /etc/rc.d/rc <==


#! /bin/bash

==> /etc/rc.d/rc0.d <==


head: /etc/rc.d/rc0.d: Is a directory

==> /etc/rc.d/rc1.d <==


head: /etc/rc.d/rc1.d: Is a directory

==> /etc/rc.d/rc2.d <==


head: /etc/rc.d/rc2.d: Is a directory

==> /etc/rc.d/rc3.d <==


head: /etc/rc.d/rc3.d: Is a directory

==> /etc/rc.d/rc4.d <==


head: /etc/rc.d/rc4.d: Is a directory

==> /etc/rc.d/rc5.d <==


head: /etc/rc.d/rc5.d: Is a directory

==> /etc/rc.d/rc6.d <==


head: /etc/rc.d/rc6.d: Is a directory

==> /etc/rc.d/rc.local <==


#!/bin/sh

==> /etc/rc.d/rc.sysinit <==


#!/bin/bash

==> /etc/rc.d/rc.sysinit.rpmsave <==


#!/bin/bash

Cuando se alimenta el comando head con múltiples archivos como argumentos este
representa de manera conveniente el nombre del archivo, seguido por el primer número
especificado de las líneas (en este caso, uno). Sin embargo, cuando el comando head
encuentra un directorio apenas se queja. Luego, elvis ejecuta el mismo comando,
redirigiendo stdout al archivorcsummary.out.
17

Standard IO and Pipe

[elvis@station elvis]$ head -1 /etc/rc.d/* > rcsummary.out


head: /etc/rc.d/init.d: Is a directory
head: /etc/rc.d/rc0.d: Is a directory
head: /etc/rc.d/rc1.d: Is a directory
head: /etc/rc.d/rc2.d: Is a directory
head: /etc/rc.d/rc3.d: Is a directory
head: /etc/rc.d/rc4.d: Is a directory
head: /etc/rc.d/rc5.d: Is a directory
head: /etc/rc.d/rc6.d: Is a directory

La mayor parte de la salida es obedientemente redirigida al archivo rcsummary.out,


pero las quejas del directorio aún se visualizan. Aunque no es obvio desde el principio,
el comando head está realmente enviando salida a dos flujos independientes. La salida
normal se escribe en la salida estándar, pero un mensaje de error se escribe en un flujo
separado llamado error estándar (a menudo abreviado stderr). Ambos flujos suelen estar
conectados a la terminal y por eso es difícil distinguirlos. No obstante, al redireccionar
stdout, la información escrita a stderr es evidente.

Redirección de stderr

Así como bash usa > para redirigir lastdout, bash usa 2> para redirigir elstderr. Por
ejemplo, elvis repite el comando head desde arriba, pero en vez de redirigir stdout a
rcsummary.out, redirige el stderr al archivo rcsummary.err.

[elvis@station elvis]$ head -1 /etc/rc.d/* 2> rcsummary.err


==> /etc/rc.d/init.d <==

==> /etc/rc.d/rc <==


#! /bin/bash

==> /etc/rc.d/rc0.d <==

==> /etc/rc.d/rc1.d <==

==> /etc/rc.d/rc2.d <==

==> /etc/rc.d/rc3.d <==

==> /etc/rc.d/rc4.d <==

==> /etc/rc.d/rc5.d <==

==> /etc/rc.d/rc6.d <==

==> /etc/rc.d/rc.local <==


#!/bin/sh

==> /etc/rc.d/rc.sysinit <==


#!/bin/bash

==> /etc/rc.d/rc.sysinit.rpmsave <==


#!/bin/bash
18

Standard IO and Pipe

La salida es el complemento al ejemplo anterior. Ahora vemos la salida normal


visualizada en la pantalla, pero sin mensajes de error. ¿A dónde fueron a parar los
mensajes de error? No costaría mucho trabajo adivinar.

[elvis@station elvis]$ cat rcsummary.err


head: /etc/rc.d/init.d: Is a directory
head: /etc/rc.d/rc0.d: Is a directory
head: /etc/rc.d/rc1.d: Is a directory
head: /etc/rc.d/rc2.d: Is a directory
head: /etc/rc.d/rc3.d: Is a directory
head: /etc/rc.d/rc4.d: Is a directory
head: /etc/rc.d/rc5.d: Is a directory
head: /etc/rc.d/rc6.d: Is a directory

En el siguiente ejemplo tanto > como 2> se utilizan para redirigir stdout y stderr de
modo independiente.

[elvis@station elvis]$ head -1 /etc/rc.d/* > rcsummary.out 2>


rcsummary.err
[elvis@station elvis]$

En este caso, la salida estándar puede encontrarse en el archivo rcsummary.out, se


pueden encontrar mensajes de error en rcsummary.err y no queda nada para mostrar en
la pantalla.

Combinación de stdout y stderr: vieja escuela

Con frecuencia, desearíamos redirigir los flujos de stdout y stderr combinados en un


sólo archivo. Como un primer intento, elvis ensaya el siguiente comando.

[elvis@station elvis]$ head -1 /etc/rc.d/* > rcsummary.both 2>


rcsummary.both

Sin embargo, tras examinar el archivo rcsummary.both, elvis no halla lo que espera.

[elvis@station elvis]$ cat rcsummary.both


head: /etc/rc.d/init.d: I
==> /etc/rc.dhead: /etc/rc.d/rc0.d: Is a directory
head: /etc/rc.d/rc1.d: Is a direc
==> head: /etc/rc.d/rc2.
==> /etc/rc.d/rc3head: /
==> /etc/rc.d/rc4.d <==

==> /etc/rc.d/rc5.d <==

==> /etc/rc.d/rc6.d <==

==> /etc/rc.d/rc.local <==


#!/bin/sh

==> /etc/rc.d/rc.sysinit <==


#!/bin/bash
19

Standard IO and Pipe

==> /etc/rc.d/rc.sysinit.rpmsave <==


#!/bin/bash

La shell bash abrió dos veces el archivo rcsummary.both, pero trató cada archivo
abierto como un archivo independiente. Cuando stdout y stderr escribieron al archivo,
sobrescribieron la información de cada cual. Lo que se necesitaba en su lugar es de
alguna manera pedirle a bash combinar de modo eficaz stderr y stdout dentro de un sólo
flujo y luego redirigir ese flujo a un archivo único. Como es de esperarse, esa forma
existe.

[elvis@station elvis]$ head -1 /etc/rc.d/* > rcsummary.both 2>&1

Aunque un poco extraño, el último símbolo 2>&1 debería considerarse como si se dijera
"tome al stderr, y envíelo a dónde stdout está actualmente". Ahora,rcsummary.both
contiene la salida esperada.

[elvis@station elvis]$ cat rcsummary.both


==> /etc/rc.d/init.d <==
head: /etc/rc.d/init.d: Is a directory

==> /etc/rc.d/rc <==


#! /bin/bash

==> /etc/rc.d/rc0.d <==


head: /etc/rc.d/rc0.d: Is a directory

==> /etc/rc.d/rc1.d <==


head: /etc/rc.d/rc1.d: Is a directory
...

Mucha de esta salida fue truncada y remplazada por "...".

Combinación de stdout y stderr: nueva escuela

Al usar 2>&1 para combinar stdout y stderr se introdujo en la shell Unix original, la shell
Bourne (sh). Dado que bash está diseñado para ser compatible con sintaxis anteriores
sh también soporta esta sintaxis. Sin embargo, la sintaxis no es conveniente. Además de
ser difícil de escribir, el orden de las redirecciones es importante. Al usar ">out.txt
2>&1" y "2>&1 >out.txt" ¡no produce el mismo efecto!

Para simplificar las cosas, bash usa >& para combinar stdin y stdout como en el
siguiente ejemplo.

[elvis@station elvis]$ head -1 /etc/rc.d/* >& rcsummary.both

Resumen

El siguiente cuadro resume la sintaxis empleada por la shell bash para redireccionar
stdin, stdout, y stderr tratados en la lección anterior y en ésta.
20

Standard IO and Pipe

Table 1. Redirección de stdin, stdout y stderr en bash

sintaxis efecto
cmd < file Redirección stdin desde file
cmd > file Redirigir stdout a file, sobrescribir file si existe.
cmd >> file Redirigir stdout a file agregando file si existe.
cmd 2> file Redirigir stderr a file sobrescribir file si existe.
cmd 2>> file Redirigir stderr a file agregando file si éste existe.
cmd > file 2>&1 Combinar stdout y stderr, y redirigirlos a file. (sintaxis portátil)
cmd >& file Combinar stdout y stderr y redirigirlos a file.(sintaxis conveniente)

Ejemplos

Uso de /dev/null para filtrar stderr

El usuario elvis recientemente ha aprendido que aparte de los directorios /home/elvis


y /tmp con los que está familiarizado, también puede tener archivos en el directorio
/var. Estos archivos en espera suelen ser archivos para correos electrónicos recibidos,
pero que no han sido vistos, o por ejemplo, también pueden ser trabajos de impresión en
espera.

Intrigado usa el comando find para buscar todos los archivos dentro del directorio /var
que posee.

[elvis@station elvis]$ find /var -user elvis


find: /var/lib/slocate: Permission denied
find: /var/lib/nfs/statd: Permission denied
find: /var/lib/xdm/authdir: Permission denied
...
find: /var/spool/lpd/three-west: Permission denied
find: /var/spool/lpd/one-east-color: Permission denied
find: /var/spool/lpd/server1: Permission denied
/var/spool/mail/elvis
find: /var/spool/at: Permission denied
...
find: /var/tux: Permission denied
find: /var/tomcat4/webapps: Permission denied

(Mucha de la salida de ese comando ha sido truncada y remplazada por "...").

Aunque el comando find reportó correctamente el archivo /var/spool/mail/elvis, la


salida es difícil de hallar dentro de todos los mensajes de error de "Permiso negado"
reportados desde varios subdirectorios de /var. Con el fin de ayudar a apartar lo útil de
lo inservible, elvis redirige stderr a algún archivo en el directorio /tmp.

[elvis@station elvis]$ find /var -user elvis 2> /tmp/foo


/var/spool/mail/elvis
21

Standard IO and Pipe

Aunque esto funciona, elvis queda con un archivo llamado /tmp/foo que en realidad no
quería. En situaciones como ésta, cuando un usuario quiere deshacerse de un flujo de
información, los usuarios experimentados de Unix suelen redirigir la salida a un seudo
dispositivo llamado /dev/null.

[elvis@station elvis]$ find /var -user elvis 2> /dev/null


/var/spool/mail/elvis

Como lo muestra el siguiente listado largo, /dev/null es un nodo de dispositivo de


caracter como aquellos de los controladores de dispositivo convencionales.

[elvis@station elvis]$ ls -l /dev/null


crw-rw-rw- 1 root root 1, 3 Jan 30 05:24 /dev/null

Cuando un usuario escribe en /dev/null, la información es apenas descartada por el


kernel. Cuando un usuario lee desde /dev/null encuentra inmediatamente un fin de
archivo. Observe que /dev/null es uno de los pocos archivos en Red Hat Enterprise
Linux que tiene permisos de escritura por defecto para todo el mundo.

Ejercicios en línea

Lab Exercise
Objetivo: Administrar de modo efectivo los flujos de la entrada estándar, la salida
estándar y el error estándar

Estimated Time: 10 mins.

Especificaciones

1. Utilice la siguiente línea de comandos para mostrar el contenido de todos los


archivos dentro del directorio /etc/X11.
2. [elvis@station elvis]$ cat /etc/X11/*
3. cat: /etc/X11/applnk: Is a directory
4. cat: /etc/X11/desktop-menus: Is a directory
5. cat: /etc/X11/fs: Is a directory
6. cat: /etc/X11/gdm: Is a directory
7. cat: /etc/X11/lbxproxy: Is a directory
8. #!/bin/sh
9.
10. PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin
11. ...
12. Repita la línea de comandos pero redirija stdout a un archivo llamado
~/stderrlab.out y stderr a un archivo llamado ~/stderrlab.err.
13. Repita el comando pero esta vez combine stdout y stderr en un sólo flujo y
redirija el flujo al archivo ~/stderrlab.both.

Deliverables

Question 1
22

Standard IO and Pipe

1. Un archivo llamado ~/stderrlab.out que contenga el flujo de stdout desde el


comando cat /etc/X11/*.
2. Un archivo llamado ~/stderrlab.err que contenga el flujo de stderr desde el
comando cat /etc/X11/*.
3. Un archivo llamado ~/stderrlab.both que contenga los flujos stdout y stderr
combinados desde el comando cat /etc/X11/*.

Capítulo 3 Tuberías

Conceptos clave

• El flujo de stdout desde un proceso puede estar conectado al flujo de stdin de


otro proceso mediante lo que Unix llama una "tubería".
• Varios de los comandos en Unix están diseñados para operar como un filtro, leer
la entrada desde stdin y enviar la salida a stdout.
• bash usa "|" para crear una tubería entre dos comandos.

Tuberías

En las lecciones anteriores, hemos visto que una salida de un proceso se puede redirigir
a cualquier parte con excepción de la pantalla de la terminal o que puede pedírsele a un
proceso que lea desde alguna parte con excepción del teclado de la terminal. Una de las
formas más comunes y eficaces para redirigir es una combinación de las dos, donde la
salida (salida estándar) de un comando es "entubada" directamente dentro de la entrada
(entrada estándar) de otro comando formando lo que Linux (y Unix) llaman tubería.

Cuando dos comandos se unen por medio de una tubería, el flujo de stdout del primer
proceso es ligado directamente a la secuencia stdin del segundo proceso para que
múltiples procesos puedan combinarse en una secuencia. Con el fin de crear unq tubería
por medio de bash, los dos comandos se unen con una barra vertical |, (en la mayoría de
los teclados este caracter está en la misma tecla de una barra invertida encima de
ENTER). A todos los procesos unidos en una tubería se les llama un grupo de proceso.

A manera de ejemplo, piense que prince está tratando de hallar los archivos más grandes
bajo el directorio /etc. Comienza por escribir el comando find para obtener un listado
de archivos con un tamaño mayor a 100kbytes.

[prince@station prince]$ find /etc -size +100k 2>/dev/null


/etc/termcap
/etc/gconf/gconf.xml.defaults/schemas/desktop/gnome/interface/%gconf.x
ml
/etc/gconf/gconf.xml.defaults/schemas/apps/mailcheck_applet/prefs/%gco
nf.xml
23

Standard IO and Pipe

/etc/gconf/gconf.xml.defaults/schemas/apps/tasklist_applet/prefs/%gcon
f.xml
...

Al observar que el comando find no parece listar los archivos en ningún orden en
particular, prince decide que sus archivos sean listados en orden alfabético. En lugar de
redirigir la salida a un archivo y luego sort el archivo, aprovecha que el comando sortse
invoca sin argumentos y espera los datos desde la entrada estándar para ordenarlos. Él
entuba la salida de su comando de búsqueda hacia sort.

[prince@station prince]$ find /etc -size +100k 2>/dev/null | sort


/etc/aep/aeptarg.bin
/etc/gconf/gconf.xml.defaults/schemas/apps/gedit-
2/preferences/editor/save/%gconf.xml
/etc/gconf/gconf.xml.defaults/schemas/apps/gnomemeeting/general/%gconf
.xml
...
/etc/makedev.d/cciss
/etc/makedev.d/dac960
/etc/squid/squid.conf
/etc/squid/squid.conf.default
/etc/termcap

Ahora los archivos están listados en orden alfabético.

Filtro de salida con grep

El comando tradicional de Unix grep se utiliza en tuberías para reducir datos a sólo las
partes "interesantes". El comando grep se analizará más tarde en un cuaderno. Aquí
presentamos el comando grep en su forma más sencilla.

El comando grep se utiliza para buscar y extraer líneas que contengan una cadena de
texto específico. Por ejemplo, a continuación, prince imprime todas las líneas que
contienen el texto "root" desde el archivo /etc/passwd.

[prince@station prince]$ grep root /etc/passwd


root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

El primer argumento para el comando grep es la cadena de texto que va a ser buscada, y
los argumentos restantes son archivos que van a ser buscados para el texto. Si el
comando grep llamado con un solo argumento (una cadena de texto para ser buscada,
pero no archivos), espera la entrada estándar como su fuente de información en la cual
operar.

En el siguiente, prince tiene tantos archivos en su directorio de inicio que está teniendo
problemas para seguirles el rastro. Trata de hallar un directorio llamadotemplates que
él creó hace unos meses. Utiliza el comando locate para encontrarlo.

[prince@station prince]$ locate templates


/etc/openldap/ldaptemplates.conf
24

Standard IO and Pipe

/usr/share/doc/libxslt-1.0.27/html/libxslt-templates.html
/usr/share/doc/libxslt-1.0.27/templates.gif
/usr/share/doc/docbook-style-xsl-1.58.1/docsrc/templates.xml
/usr/share/man/man5/ldaptemplates.conf.5.gz
/usr/share/man/man3/ldap_free_templates.3.gz
/usr/share/man/man3/ldap_init_templates_buf.3.gz
/usr/share/man/man3/ldap_init_templates.3.gz
...

Por desgracia para prince, hay muchos archivos en el sistema que contienen el
textotemplates en su nombre y se confunde con líneas y líneas de salida. Con el fin de
reducir la información a archivos más importantes, prince toma la stdout desde el
comando locate y crea una tubería para la stdin del comandogrep, "buscando" la
palabra "prince".

[prince@station 010_section_discussion]$ locate templates | grep


prince
/home/prince/.kde/share/apps/quanta/templates
/home/prince/proj/templates

Dado que al comando grep no se le da un archivo para buscar, espera a la stdin, donde
encuentra el flujo de stdout del comando locate. Al filtrar el flujo, grep sólo duplica a
sus líneas stdout que coincidieron con el texto especificado, "prince". El resto fue
descartado. El usuario prince encuentra fácilmente su directorio bajo ~/proj, como
también otro directorio creado por la aplicación quanta.

Tuberías y stderr

En el próximo ejemplo, prince quiere ver dónde aparece en los archivos de


configuración del sistema y utiliza la herramienta "grep" con su nombre en el directorio
/etc.

[prince@station prince]$ grep prince /etc/*


grep: /etc/aliases.db: Permission denied
grep: /etc/at.deny: Permission denied
grep: /etc/default: Permission denied
/etc/group:music:x:205:elvis,blondie,prince,madonna,student
/etc/group:prince:x:502:
grep: /etc/group-: Permission denied
grep: /etc/group.lock: Permission denied
...
grep: /etc/lvmtab: Permission denied
/etc/passwd:prince:x:502:502::/home/prince:/bin/bash
grep: /etc/passwd-: Permission denied
grep: /etc/passwd.lock: Permission denied
...
grep: /etc/sudoers: Permission denied
/etc/termcap:# From: John Doe <jd@princeton.edu>
grep: /etc/vsftpd.conf.rpmsave: Permission denied

De nuevo, prince está abrumado con tanta salida desde este comando. Emplea el mismo
truco de buscar con "grep" todas las líneas que contengan la palabra "passwd".
25

Standard IO and Pipe

[prince@station prince]$ grep prince /etc/* | grep passwd


grep: /etc/aliases.db: Permission denied
grep: /etc/at.deny: Permission denied
grep: /etc/default: Permission denied
grep: /etc/group-: Permission denied
grep: /etc/group.lock: Permission denied
...
grep: /etc/lvmtab: Permission denied
/etc/passwd:prince:x:502:502::/home/prince:/bin/bash
grep: /etc/passwd-: Permission denied
grep: /etc/passwd.lock: Permission denied
...

Aunque stdout desde el primer comando con grep fue filtrado correctamente, stderr no
se afectó y aún se muestra en la pantalla. ¿Cómo le iría a prince si suprimiera también
stderr?

Comandos como filtros

El concepto de tubería se extiende naturalmente para que múltiples comandos se puedan


usar en conjunto, cada información de lectura desde stdin, de alguna manera modifica o
filtra la información y pasa el resultado a stdout. En un próximo cuaderno, usted verá
que hay muchos comandos estándar de Linux (y Unix) diseñados para este propósito,
incluyendo algunos de los cuales usted ya conoce: grep, head, tail, cut, sort, sed, y
awk por nombrar unos cuantos.

Ejemplos

Listado de procesos por nombre

A menudo, quisiéramos listar información acerca de procesos que están ejecutando un


comando específico. Aunque ps aux presenta un montón de información sobre procesos
actuales en ejecución, el número de procesos ejecutándose en la máquina puede resultar
en una la salida abrumadora. El comando grep puede ayudar a simplificar la salida.

A continuación, prince quiere listar información sobre los procesos que están
implementando su servidor de red, el comando httpd. Él lista todos los procesos pero
luego reduce la salida a solo aquellas líneas que contengan el texto httpd.

[prince@station prince]$ ps aux | grep httpd


root 889 0.0 0.0 18248 100 ? S Sep22 0:00
/usr/sbin/httpd
apache 907 0.0 0.5 18436 1320 ? S Sep22 0:00
/usr/sbin/httpd
apache 913 0.0 0.7 18436 1952 ? S Sep22 0:00
/usr/sbin/httpd
apache 914 0.0 0.5 18436 1332 ? S Sep22 0:00
/usr/sbin/httpd
apache 1979 0.0 0.5 18360 1524 ? S Sep22 0:00
/usr/sbin/httpd
apache 1980 0.0 0.8 18388 2140 ? S Sep22 0:00
/usr/sbin/httpd
26

Standard IO and Pipe

apache 1981 0.0 0.5 18360 1524 ? S Sep22 0:00


/usr/sbin/httpd
prince 4905 0.0 0.2 3572 640 pts/1 S 06:19 0:00 grep
httpd

Búsqueda eficaz en el historial de comandos

Recientemente, el usuario prince tomó un buen tiempo construyendo una línea de


comandos find, la cual listaba todos los archivos grandes bajo el directorio /etc,
incluyendo el tamaño. En lugar de repetir su esfuerzo prefiere ver si el comando está
aún en su historial. Dado que su historial contiene cientos de líneas, utiliza el comando
grep para ayudar a reducir la salida.

[prince@station prince]$ history | grep find


102 find /var -user elvis
175 find -exec file {} \;
434 find /etc -name *.conf | head
675 find /etc -size +100k
680 find /etc -size +100k -exec ls -s {} \; 2>/dev/null
682 find -size +100k /etc
683 find /etc -size +100k
690 history | grep find

Ahora localiza la línea de comandos que quería y utiliza la sustitución del historial para
repetir el comando con facilidad.

[prince@station prince]$ !680


find /etc -size +100k -exec ls -s {} \; 2>/dev/null
728 /etc/termcap
132
/etc/gconf/gconf.xml.defaults/schemas/desktop/gnome/interface/%gconf.x
ml
304 /etc/gconf/schemas/gedit.schemas
...

Filosofía de Unix: Herramientas sencillas que funcionan bien juntas

Linux, al igual que Unix, se basa fundamentalmente en la filosofía de que los sistemas
complejos deberían crearse de componentes simples y de componentes especializados
que funcionen fácilmente entre sí. Siguiendo esta filosofía, muchos programas estándar
de Linux están diseñados para operar como filtros, leyendo información desde una
fuente estándar manipulando los datos y entregando el resultado a un destino estándar.

Esta filosofía es importante para ilustrar el uso de un ejemplo largo y detallado. El


ejemplo utilizará comandos con los que usted aún no está familiarizado. No se preocupe
de los detalles de cómo usar los comandos, en su lugar, enfóquese en cómo funcionan
juntos, cada comando realiza un pequeña parte para producir el resultado deseado.

Suponga que un administrador de sistemas está examinando los mensajes de alquiler


DHCP en un archivo de registro bien conocido, /var/log/messages. Si usted no está
familiarizado con DHCP, es el protocolo por medio del cual las direcciones IP se
27

Standard IO and Pipe

pueden asignar a máquinas basadas en la dirección de hardware (MAC) construídas


dentro de una tarjeta de Ethernet de la máquina. En las siguientes líneas de
/var/log/messages enfóquese en la línea que contiene la palabra DHCPOFFER.
Observe que la dirección IP 192.168.0.11 se está ofreciendo a la tarjeta de Ethernet con
la dirección de hardware de 00:08:74:37:c5:c3.

...
May 27 12:18:21 server1 dhcpd: DHCPACK on 192.168.0.110 to
00:09:6b:d0:ce:8f via eth0
May 27 12:18:27 server1 login(pam_unix)[1981]: session closed for user
root
May 27 12:19:15 server1 named[24350]: listening on IPv4 interface
eth1, 192.168.22.20#53
May 27 12:19:21 server1 vsftpd: warning: can't get client address: Bad
file descriptor
May 27 12:19:21 server1 last message repeated 3 times
May 27 12:20:27 server1 dhcpd: DHCPDISCOVER from 00:08:74:37:c5:c3 via
eth0
May 27 12:20:27 server1 dhcpd: DHCPOFFER on 192.168.0.11 to
00:08:74:37:c5:c3 via eth0
May 27 12:20:27 server1 dhcpd: DHCPREQUEST for 192.168.0.11
(192.168.0.254) from 00:08:74:37:c5:c3 via eth0
...

Sin preocuparse por detalles del protocolo de DHCP, suponga que el administrador
deseara extraer una lista de direcciones IP y las direcciones de hardware que se ofrecen
en el archivo de registro. Un administrador experimentado podría emplear el siguiente
método.

Dándose cuenta que el archivo /var/log/message es un archivo muy grande, en este


caso más de 1000 líneas de longitud, el administrador primero utiliza el comando grep
para reducir la información a las líneas pertinentes.

[root@station log]# grep DHCPOFFER messages


May 27 11:46:22 server1 dhcpd: DHCPOFFER on 192.168.0.1 to
00:08:74:d9:41:9e via eth0
May 27 11:46:22 server1 dhcpd: DHCPOFFER on 192.168.0.1 to
00:08:74:d9:41:9e via eth0
May 27 11:46:30 server1 dhcpd: DHCPOFFER on 192.168.0.1 to
00:08:74:d9:41:9e via eth0
May 27 11:46:30 server1 dhcpd: DHCPOFFER on 192.168.0.1 to
00:08:74:d9:41:9e via eth0
May 27 11:48:40 server1 dhcpd: DHCPOFFER on 192.168.0.2 to
00:08:74:d9:41:32 via eth0
...

Este es un comienzo pero el administrador aún está manejando demasiada información


(la toma de pantalla de arriba sólo lista las primeras 5 de 90 líneas producidas por este
comando). Con el fin de extraer sólo la información pertinente, es decirla dirección IP y
la dirección de hardware, el administrador toma la salida del comando grep y la entuba
a un comando llamado sed, el cual quita las primeras palabras de cada línea.

[root@station log]# grep DHCPOFFER messages | sed "s/^.*on //"


28

Standard IO and Pipe

192.168.0.1 to 00:08:74:d9:41:9e via eth0


192.168.0.1 to 00:08:74:d9:41:9e via eth0
192.168.0.1 to 00:08:74:d9:41:9e via eth0
192.168.0.1 to 00:08:74:d9:41:9e via eth0
192.168.0.2 to 00:08:74:d9:41:32 via eth0
192.168.0.2 to 00:08:74:d9:41:32 via eth0
192.168.0.2 to 00:08:74:d9:41:32 via eth0
192.168.0.2 to 00:08:74:d9:41:32 via eth0
192.168.0.3 to 00:08:74:d9:40:a4 via eth0
...

Si no está familiarizado con el comando sed (muy probablemente no lo está), no se


preocupe por los detalles, sólo observe que el argumento de sed quitó el texto inicial de
cada línea hasta la palabra "on". Como no queda mucho texto, el administrador toma la
salida de esta combinación grep-sed y la entuba al comando llamado awk.

[root@station log]$ grep DHCPOFFER messages | sed "s/^.*on //


" | awk '{print $1,$3}'
...
192.168.0.14 00:08:74:34:fe:bc
192.168.0.5 00:08:74:34:fd:36
192.168.0.15 00:08:74:37:c8:eb
192.168.0.15 00:08:74:37:c8:eb
192.168.0.6 00:08:74:d9:41:a3
192.168.0.16 00:08:74:d9:41:ac
192.168.0.7 00:08:74:d9:41:53
192.168.0.16 00:08:74:d9:41:ac
192.168.0.17 00:08:74:35:00:e3
...

De nuevo, no se preocupe por los detalles del comando awk, observe que el resultado
era extraer la primera y tercera columna de la salida anterior. Con el fin de ordenar la
información, y quitar las líneas que se duplican, el administrador toma la salida desde la
cadena y la entuba a través de los comandos sort y uniq.

[root@station log]$ grep DHCPOFFER messages | sed "s/^.*on //


" | awk '{print $1,$3}' | sort | uniq
192.168.0.10 00:08:74:d9:40:95
192.168.0.1 00:08:74:d9:41:9e
192.168.0.110 00:09:6b:d0:ce:8f
192.168.0.11 00:08:74:37:c5:c3
192.168.0.12 00:08:74:d9:41:dd
192.168.0.13 00:08:74:35:00:d0
192.168.0.14 00:08:74:34:fe:bc
192.168.0.15 00:08:74:37:c8:eb
192.168.0.16 00:08:74:d9:41:ac
192.168.0.17 00:08:74:35:00:e3
192.168.0.2 00:08:74:d9:41:32
192.168.0.3 00:08:74:d9:40:a4
192.168.0.4 00:08:74:d9:3f:7f
192.168.0.5 00:08:74:34:fd:36
192.168.0.6 00:08:74:d9:41:a3
192.168.0.7 00:08:74:d9:41:53
192.168.0.8 00:08:74:d9:41:7b
192.168.0.9 00:08:74:35:00:1f
29

Standard IO and Pipe

Esta es casi la lista que el administrador quería pero el comando sort no funcionó bien.
La información está ordenada, pero en orden alfabético, no por dirección IP. El
administrador modifica el comando sort con un par de opciones para especificar el
orden de modo numérico, pulsando en el cuarto campo, donde los campos están
separados por un punto.

[root@station log]$ grep DHCPOFFER messages | sed "s/^.*on //" | awk


'{print $1,
$3}' | sort -n -k4 -t. | uniq
192.168.0.1 00:08:74:d9:41:9e
192.168.0.2 00:08:74:d9:41:32
192.168.0.3 00:08:74:d9:40:a4
192.168.0.4 00:08:74:d9:3f:7f
192.168.0.5 00:08:74:34:fd:36
192.168.0.6 00:08:74:d9:41:a3
192.168.0.7 00:08:74:d9:41:53
192.168.0.8 00:08:74:d9:41:7b
192.168.0.9 00:08:74:35:00:1f
192.168.0.10 00:08:74:d9:40:95
192.168.0.11 00:08:74:37:c5:c3
192.168.0.12 00:08:74:d9:41:dd
192.168.0.13 00:08:74:35:00:d0
192.168.0.14 00:08:74:34:fe:bc
192.168.0.15 00:08:74:37:c8:eb
192.168.0.16 00:08:74:d9:41:ac
192.168.0.17 00:08:74:35:00:e3
192.168.0.110 00:09:6b:d0:ce:8f

Esta es la lista que el administrador quería, en el orden deseado. Redirige todo esta
salida a un archivo en su directorio de inicio.

[root@station log]$ grep DHCPOFFER messages | sed "s/^.*on //" | awk


'{print $1
,$3}' | sort -n -k4 -t. | uniq > ~/ip_dhcp.txt

En este ejemplo, un administrador (experimentado) que navega un archivo de registro


pudo emplear unos pocos minutos y desarrollar una cadena de comandos que filtraran la
información original a la información precisa deseada. Hizo esto mediante un puñado de
herramientas que son para la mayoría de los administradores de Unix una caja de
herramientas mentales: grep, sed, awk, sort, yuniq.

Si el administrador utilizara un sistema operativo que no estuviera diseñado en torno a


esta filosofía de "pequeñas herramientas que trabajan juntas", habría necesitado
depender de algún programador para desarrollar la utilidad ip_mac_extractor y
posiblemente depender de ese programador para crear también una interfaz gráfica de
usuario. En cambio, como pudo utilizar la flexibilidad de la línea de comando, pudo
también manejar la información por sí mismo.

Ejercicios en línea

Lab Exercise
30

Standard IO and Pipe

Objetivo: Usar tuberías para filtrar información de modo efectivo.

Estimated Time: 10 mins.

Especificaciones

1. Usted desearía crear una lista ordenada de todos los servicios de TCP services
que se encuentran en el archivo /etc/services. Entube la salida del
comandogrep tcp /etc/services dentro del comando sort. Redirija la salida de
esta tubería dentro del archivo ~/pipelab.txt.
2. Mediante el visualizador de página less desearía navegar la salida del comando
ls -R /, viendo sólo archivos que contengan la letra s. Escriba un línea de
comando con dos tuberías para encadenar los comandos ls -R /, grep s, y less.
Abandone el visualizador de página less en el primer plano mientras califica su
ejercicio.

Deliverables

A title

Question 1

1. Un archivo ~/pipelab.txt que contenga la salida del comando grep tcp


/etc/services entubado a través del comando sort.
2. Un visualizador de página activo less que está navegando la salida del comando
ls -R / entubado a través del comando grep s.

Limpieza

Después de calificar su ejercicio puede salir del paginador less.

También podría gustarte