Linux avanzado: piping (tuberías)
En Linux y casi todos los sistemas operativos (incluido Windows), saber manejar bien la consola es fundamental. Ningún usuario puede considerarse avanzado si no conoce las posibilidades que brinda la consola y sobre todo cómo usarlas y combinarlas. Hay veces (muchas) en que la consola brinda una potencia y una automatización de tareas que la interfaz gráfica sólo puede soñar. Queda mucho para que la interfaz gráfica tenga la potencia de la consola.
La filosofía UNIX trata de presentar herramientas con una funcionalidad específica y reducida. Esto permite múltiples combinaciones de dichas herramientas para realizar asuntos complicados. Una forma de combinar las herramientas que nos proporciona UNIX es usando pipes (tuberías). El concepto es el mismo que las tuberías en programación POSIX para comunicar procesos (para los que ya conozcan este tema).
Una tubería normalmente se representa con el carácter | (AltGr-1 en teclado español, código ASCII 124). La tubería permite pasar la salida de un proceso a otro proceso que la toma como entrada. Veamos algún ejemplo.
La herramienta grep es un pilar de cualquier sistema *IX. Permite filtrar los resultados. Para ilustrar su utilidad, creemos un fichero de texto con el siguiente contenido:
y lo guardamos como prueba. Ahora supongamos que sólo queremos listar los nombres que contengan la letra 'o'.
m0skit0@sodiet:~/Temp$ cat prueba
Alfonso
Raquel
Pepe
Epifanio
Fidedigno
Obsoleto
m0skit0@sodiet:~/Temp$ cat prueba | grep o
Alfonso
Epifanio
Fidedigno
Obsoleto
Ahora podría volver a filtar para obtener sólo los nombre que contengan 'o' y 'n':
m0skit0@sodiet:~/Temp$ cat prueba | grep o | grep n
Alfonso
Epifanio
Fidedigno
Con otro ejemplo más útil: listar procesos en ejecución:
m0skit0@sodiet:~/Temp$ ps -A
PID TTY TIME CMD
1 ? 00:00:01 init
2 ? 00:00:00 kthreadd
3 ? 00:00:00 migration/0
4 ? 00:00:00 ksoftirqd/0
5 ? 00:00:00 watchdog/0
6 ? 00:00:00 events/0
[...]
2042 ? 00:00:00 gvfsd-trash
2045 ? 00:00:00 e-calendar-fact
2055 ? 00:00:00 e-addressbook-f
2064 ? 00:00:00 gvfsd-burn
2088 tty1 00:07:30 firefox-bin
2957 tty1 00:00:00 gnome-terminal
2958 tty1 00:00:00 gnome-pty-helpe
2959 pts/0 00:00:00 bash
3407 ? 00:00:00 flush-8:16
3466 pts/0 00:00:00 ps
Este comando lista todos los procesos en ejecución. La salida es muy engorrosa para buscar un proceso en concreto. Pero si usamos grep:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal
2957 tty1 00:00:00 gnome-terminal
Ahora podríamos terminar este proceso con kill:
m0skit0@sodiet:~$ kill -TERM 2957
Pero esto se puede mejorar. ¿Podríamos conseguir mandar a kill el número de proceso directamente usando una sola línea de comando?
Primero tendremos que extraer el número de proceso de la salida que nos brinda ps -A | grep gnome-terminal. Para ello tenemos a la maravillosa utilidad cut. cut corta, como su nombre bien indica, una línea (o varias) según un cierto formato. Para demostrar su funcionamiento, nos vamos a sustituir el anterior fichero prueba con este nuevo contenido:
Ahora veamos qué pasa si delimitamos por el carácter espacio ' '. Con -d le indicamos el carácter que queremos usar como delimitador (tabulación por defecto) y con -f qué parte del corte queremos.
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 1
1
2
3
4
5
6
Como véis, hemos cortado todo lo previo al primer espacio. Veamos que pasa si cogemos entre el primer y el segundo espacio:
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 2
García,
Pérez,
Gómez,
Segundo,
Negro,
Cuevas,
Hemos cogido los apellidos. Si queremos suprimir la coma, podríamos usar otra vez cut usando la coma como delimitador. Por ejemplo:
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 2 | cut -d ',' -f 1
García
Pérez
Gómez
Segundo
Negro
Cuevas
Volviendo entonces a nuestro asunto, queremos sacar únicamente el número de proceso de la siguiente salida:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal
3517 tty1 00:00:00 gnome-terminal
Como ya hemos visto, podemos cortar por espacios y coger el segundo campo (lo que hay entre el primer y el segundo espacio):
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2
3517
Ahora bien, kill no soporta leer por la entrada estándar, le da dolor de cabeza:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2 | kill -TERM
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2 | kill -TERM -
bash: kill: -: arguments must be process or job IDs
Por tanto no podemos usar tuberías (si alguien sabe cómo hacerlo por tuberías, que nos enseñe). En este caso usaré backticks -> ` (no es suciedad en la pantalla, es una tilde al revés, código ASCII 96). Esto hace que bash sustituya todo lo que vaya entre este símbolo por el resultado de su ejecución. Esto sí se lo podemos pasar a kill (y a cualquier comando):
m0skit0@sodiet:~$ kill -TERM `ps -A | grep gnome-terminal | cut -d ' ' -f 2`
Espero que os haya resultado útil el ver las posibilidades y las limitaciones de las tuberías. Los comentarios son los bienvenidos.
La filosofía UNIX trata de presentar herramientas con una funcionalidad específica y reducida. Esto permite múltiples combinaciones de dichas herramientas para realizar asuntos complicados. Una forma de combinar las herramientas que nos proporciona UNIX es usando pipes (tuberías). El concepto es el mismo que las tuberías en programación POSIX para comunicar procesos (para los que ya conozcan este tema).
Una tubería normalmente se representa con el carácter | (AltGr-1 en teclado español, código ASCII 124). La tubería permite pasar la salida de un proceso a otro proceso que la toma como entrada. Veamos algún ejemplo.
La herramienta grep es un pilar de cualquier sistema *IX. Permite filtrar los resultados. Para ilustrar su utilidad, creemos un fichero de texto con el siguiente contenido:
Alfonso
Raquel
Pepe
Epifanio
Fidedigno
Obsoleto
Raquel
Pepe
Epifanio
Fidedigno
Obsoleto
y lo guardamos como prueba. Ahora supongamos que sólo queremos listar los nombres que contengan la letra 'o'.
m0skit0@sodiet:~/Temp$ cat prueba
Alfonso
Raquel
Pepe
Epifanio
Fidedigno
Obsoleto
m0skit0@sodiet:~/Temp$ cat prueba | grep o
Alfonso
Epifanio
Fidedigno
Obsoleto
Ahora podría volver a filtar para obtener sólo los nombre que contengan 'o' y 'n':
m0skit0@sodiet:~/Temp$ cat prueba | grep o | grep n
Alfonso
Epifanio
Fidedigno
Con otro ejemplo más útil: listar procesos en ejecución:
m0skit0@sodiet:~/Temp$ ps -A
PID TTY TIME CMD
1 ? 00:00:01 init
2 ? 00:00:00 kthreadd
3 ? 00:00:00 migration/0
4 ? 00:00:00 ksoftirqd/0
5 ? 00:00:00 watchdog/0
6 ? 00:00:00 events/0
[...]
2042 ? 00:00:00 gvfsd-trash
2045 ? 00:00:00 e-calendar-fact
2055 ? 00:00:00 e-addressbook-f
2064 ? 00:00:00 gvfsd-burn
2088 tty1 00:07:30 firefox-bin
2957 tty1 00:00:00 gnome-terminal
2958 tty1 00:00:00 gnome-pty-helpe
2959 pts/0 00:00:00 bash
3407 ? 00:00:00 flush-8:16
3466 pts/0 00:00:00 ps
Este comando lista todos los procesos en ejecución. La salida es muy engorrosa para buscar un proceso en concreto. Pero si usamos grep:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal
2957 tty1 00:00:00 gnome-terminal
Ahora podríamos terminar este proceso con kill:
m0skit0@sodiet:~$ kill -TERM 2957
Pero esto se puede mejorar. ¿Podríamos conseguir mandar a kill el número de proceso directamente usando una sola línea de comando?
Primero tendremos que extraer el número de proceso de la salida que nos brinda ps -A | grep gnome-terminal. Para ello tenemos a la maravillosa utilidad cut. cut corta, como su nombre bien indica, una línea (o varias) según un cierto formato. Para demostrar su funcionamiento, nos vamos a sustituir el anterior fichero prueba con este nuevo contenido:
1 García, Alfonso
2 Pérez, Raquel
3 Gómez, Pepe
4 Segundo, Epifanio
5 Negro, Fidedigno
6 Cuevas, Obsoleto
2 Pérez, Raquel
3 Gómez, Pepe
4 Segundo, Epifanio
5 Negro, Fidedigno
6 Cuevas, Obsoleto
Ahora veamos qué pasa si delimitamos por el carácter espacio ' '. Con -d le indicamos el carácter que queremos usar como delimitador (tabulación por defecto) y con -f qué parte del corte queremos.
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 1
1
2
3
4
5
6
Como véis, hemos cortado todo lo previo al primer espacio. Veamos que pasa si cogemos entre el primer y el segundo espacio:
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 2
García,
Pérez,
Gómez,
Segundo,
Negro,
Cuevas,
Hemos cogido los apellidos. Si queremos suprimir la coma, podríamos usar otra vez cut usando la coma como delimitador. Por ejemplo:
m0skit0@sodiet:~/Temp$ cat prueba | cut -d ' ' -f 2 | cut -d ',' -f 1
García
Pérez
Gómez
Segundo
Negro
Cuevas
Volviendo entonces a nuestro asunto, queremos sacar únicamente el número de proceso de la siguiente salida:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal
3517 tty1 00:00:00 gnome-terminal
Como ya hemos visto, podemos cortar por espacios y coger el segundo campo (lo que hay entre el primer y el segundo espacio):
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2
3517
Ahora bien, kill no soporta leer por la entrada estándar, le da dolor de cabeza:
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2 | kill -TERM
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
m0skit0@sodiet:~/Temp$ ps -A | grep gnome-terminal | cut -d ' ' -f 2 | kill -TERM -
bash: kill: -: arguments must be process or job IDs
Por tanto no podemos usar tuberías (si alguien sabe cómo hacerlo por tuberías, que nos enseñe). En este caso usaré backticks -> ` (no es suciedad en la pantalla, es una tilde al revés, código ASCII 96). Esto hace que bash sustituya todo lo que vaya entre este símbolo por el resultado de su ejecución. Esto sí se lo podemos pasar a kill (y a cualquier comando):
m0skit0@sodiet:~$ kill -TERM `ps -A | grep gnome-terminal | cut -d ' ' -f 2`
Espero que os haya resultado útil el ver las posibilidades y las limitaciones de las tuberías. Los comentarios son los bienvenidos.
Hola :) Me ha gustado mucho. Y es muy útil para ayudarte a comprender conceptos más avanzados.
ReplyDeleteSaludos
Gracias Yn$an€ :)
ReplyDeleteGracias, es muy útil saber estas cosas
ReplyDeleteTenias tiempo de no publicar nada :)
Gracias a ti por comentar. Disculpa la poca actividad, estoy bastante liado ^^
ReplyDeleteYour thee best
ReplyDelete