1. 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. 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
root
1 0.0 0.0 1380
76 ?
root
2 0.0 0.0
0
0 ?
[keventd]
root
3 0.0 0.0
0
0 ?
STAT START
S
Jun02
SW
Jun02
TIME COMMAND
0:04 init [
0:00
SW
0:00 [kapmd]
Jun02
3. 3
Standard IO and Pipe
root
4 0.0
[ksoftirqd_CPU0]
root
9 0.0
[bdflush]
root
5 0.0
[kswapd]
root
6 0.0
[kscand/DMA]
root
7 0.0
[kscand/Normal]
root
8 0.0
[kscand/HighMem]
0.0
0
0 ?
SWN
Jun02
0:00
0.0
0
0 ?
SW
Jun02
0:00
0.0
0
0 ?
SW
Jun02
0:00
0.0
0
0 ?
SW
Jun02
0:00
0.0
0
0 ?
SW
Jun02
0:37
0.0
0
0 ?
SW
Jun02
0:00
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
root
1 0.0 0.0 1380
76 ?
S
root
2 0.0 0.0
0
0 ?
SW
[keventd]
root
3 0.0 0.0
0
0 ?
SW
root
4 0.0 0.0
0
0 ?
SWN
[ksoftirqd_CPU0]
root
9 0.0 0.0
0
0 ?
SW
[bdflush]
root
5 0.0 0.0
0
0 ?
SW
[kswapd]
root
6 0.0 0.0
0
0 ?
SW
[kscand/DMA]
root
7 0.0 0.0
0
0 ?
SW
[kscand/Normal]
START
Jun02
Jun02
TIME COMMAND
0:04 init [
0:00
Jun02
Jun02
0:00 [kapmd]
0:00
Jun02
0:00
Jun02
0:00
Jun02
0:00
Jun02
0:37
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. 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
sysadmin@example.com
-s "Computer is sluggish"
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. 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
lrwx-----1 elvis
elvis
64
lrwx-----1 elvis
elvis
64
lr-x-----1 elvis
elvis
64
/etc/termcap
Sep
Sep
Sep
Sep
13
13
13
13
06:42
06:42
06:42
06:42
0
1
2
3
-> /dev/tty1
-> /dev/tty1
-> /dev/tty1
->
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. 6
Standard IO and Pipe
Flujo
Descriptor Abbreviation
Entrada estándar 0
stdin
Salida estándar 1
stdout
Error estándar
stderr
2
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-----/dev/pts/1
lr-x-----/etc/termcap
1 elvis
elvis
64 Sep 13 07:05 2 ->
1 elvis
elvis
64 Sep 13 07:05 3 ->
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
[elvis@station elvis]$ ps
PID TTY
TIME CMD
1368 pts/1
00:00:00 bash
cat </etc/termcap >/tmp/foo
7. 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. 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. 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. 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. 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. 12
Standard IO and Pipe
procs
cpu
r b
swpd
sy id wa
2 6
0
0 97 1
1 5
0
2 0 93
1 6
0
16 0 69
0 6
0
8 0 85
0 6
0
1 0 93
3 3
0
16 0 74
3 3
0
16 0 76
1 6
0
4 0 87
memory
free
buff
swap
io
system
cache
si
so
bi
bo
in
cs us
17348
65604 277768
0
0
15
16
126
221
1
15736
66008 277788
0
0
376
6269
314
725
5
11496
67224 277392
0
0
1216
8
422
1533 15
10492
67944 277676
0
0
940
28
338
1193
7
10168
68324 277644
0
0
576
0
261
992
6
8848
69424 277864
0
0
1252
64
429
1386 10
8056
70188 277892
0
0
1068
1148
422
1215
8
12248
71084 277636
0
0
940
28
341
1275
9
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. 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. 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, noninterlaced
[student@station student]$ eog gnuplot.png
15. 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. 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*
rc1.d/ rc3.d/ rc5.d/ rc.local*
[elvis@station elvis]$ head -1 /etc/rc.d/*
==> /etc/rc.d/init.d <==
head: /etc/rc.d/init.d: Is a directory
rc.sysinit*
rc.sysinit.rpmsave*
==> /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. 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. 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. 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. 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. 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-rw1 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.
3.
4.
5.
6.
7.
8.
9.
10.
11.
[elvis@station elvis]$ cat /etc/X11/*
cat: /etc/X11/applnk: Is a directory
cat: /etc/X11/desktop-menus: Is a directory
cat: /etc/X11/fs: Is a directory
cat: /etc/X11/gdm: Is a directory
cat: /etc/X11/lbxproxy: Is a directory
#!/bin/sh
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin
...
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. 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. 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/gedit2/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. 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
prince
/home/prince/.kde/share/apps/quanta/templates
/home/prince/proj/templates
templates
|
grep
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. 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
root
889
0.0
0.0
/usr/sbin/httpd
apache
907
0.0
0.5
/usr/sbin/httpd
apache
913
0.0
0.7
/usr/sbin/httpd
apache
914
0.0
0.5
/usr/sbin/httpd
apache
1979
0.0
0.5
/usr/sbin/httpd
apache
1980
0.0
0.8
/usr/sbin/httpd
aux | grep httpd
18248
100 ?
S
Sep22
0:00
18436 1320 ?
S
Sep22
0:00
18436 1952 ?
S
Sep22
0:00
18436 1332 ?
S
Sep22
0:00
18360 1524 ?
S
Sep22
0:00
18388 2140 ?
S
Sep22
0:00
26. 26
Standard IO and Pipe
apache
1981
0.0
0.5 18360 1524 ?
/usr/sbin/httpd
prince
4905 0.0 0.2 3572 640 pts/1
httpd
S
S
Sep22
06:19
0:00
0:00 grep
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. 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
00:08:74:d9:41:9e via eth0
May
27
11:46:22
server1
dhcpd:
DHCPOFFER
00:08:74:d9:41:9e via eth0
May
27
11:46:30
server1
dhcpd:
DHCPOFFER
00:08:74:d9:41:9e via eth0
May
27
11:46:30
server1
dhcpd:
DHCPOFFER
00:08:74:d9:41:9e via eth0
May
27
11:48:40
server1
dhcpd:
DHCPOFFER
00:08:74:d9:41:32 via eth0
...
on
192.168.0.1
to
on
192.168.0.1
to
on
192.168.0.1
to
on
192.168.0.1
to
on
192.168.0.2
to
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. 28
Standard IO and Pipe
192.168.0.1
192.168.0.1
192.168.0.1
192.168.0.1
192.168.0.2
192.168.0.2
192.168.0.2
192.168.0.2
192.168.0.3
...
to
to
to
to
to
to
to
to
to
00:08:74:d9:41:9e
00:08:74:d9:41:9e
00:08:74:d9:41:9e
00:08:74:d9:41:9e
00:08:74:d9:41:32
00:08:74:d9:41:32
00:08:74:d9:41:32
00:08:74:d9:41:32
00:08:74:d9:40:a4
via
via
via
via
via
via
via
via
via
eth0
eth0
eth0
eth0
eth0
eth0
eth0
eth0
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. 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. 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.