Colección de Ayudas
En construcción para siempre!
Esta página, a pesar de estar dentro del contenido de Conversa de Bar, nunca fue publicada en la
Linux Magazine. Trátase de artículos que escribí para otras publicaciones, ayudas útiles que leí navegando por la internet (y en este caso con los debidos créditos), contribuciones de estas personas de
Software Libre, maravilloso y siempre pronto a ayudar y de la imperdible
"Lista de Shell Script"
Pasando parámetros con xargs
Existe un comando, cuya función principal es construir listas de parámetros y pasarlas para la ejecución de otros programas o instrucciones. Este comando es el
xargs y debe ser usado de la siguiente manera:
xargs [comando [argumento inicial]]
En el caso de que el comando, que pode ser inclusive un
script Shell, sea omitido, será usado por
default el
echo.
El
xargs combina el argumento inicial con los argumentos recibidos de la entrada padrón, de forma de ejecutar el comando especificado una o más veces.
Exemplo:
Vamos a buscar en todos los archivos abajo de un determinado directorio una cadena de caracteres usando el comando
find con la opción
-type f para buscar solamente los archivos normales, despreciando directorios, archivos especiales, archivos de ligaciones, etc, y vamos hacerla más general recibiendo el nombre del directorio inicial y la cadena a ser buscada como parámetros. Para eso hacemos:
$ cat grepr
#
# Grep recursivo
# Busca la cadena de caracteres definida en $2 a partir del directorio $1
#
find $1 -type f -print|xargs grep -l "$2"
Ejecutando este script buscamos, a partir del directório definido en la variable
$1, todos los archivos que contengan la cadena definida en la variable
$2.
Exactamente la misma cosa podría ser hecho si la línea del programa fuera la seguinte:
find $1 -type f -exec grep -l "$2" {} \;
El primer proceso tiene dos grandes desventajas sobre el anterior:
- La primera es bastante visible: el tiempo de ejecución de este método es muy superior al segundo, eso porque el
grep será hecho en cada archivo que le sea pasado por el find, uno a uno, al paso que con el xargs, será pasada toda, o en la peor de las hipótesis, la mayor parte posible, de la lista de archivos generada por el find;
- Dependiendo de la cantidad de archivos encontrados que atiendan al
find, podremos recibir aquel famoso y fatídico mensaje de error Too many arguments indicando una sobrecarga en la pila de ejecución del grep. Como fue dicho en el iten anterior, si usamos el xargs, él pasará para el grep la mayor cantidad de parámetros posible, suficiente para no causar este error y en caso necesario ejecutará el grep más de una vez.

Atención aquellas personas de linux que usan el
ls colorido como puerta de tintoraría: en los ejemplos a seguir que incluyen esta instrucción, deben usar la opción
--color=none, si no existen grandes posibilidades de que los resultados no sean los esperados.
Vamos ahora a analizar un ejemplo que es más o menos lo contrario de este que acabamos de ver. Esta vez, vamos a hacer un
script para borrar todos los archivos del directório actual y pertenecientes a un determinado usuario.
La primera idea que surge es, como en el caso anterior, usar un comando
find, de la siguiente manera:
find . -user cara -exec rm -f {} \;
Casi estaría correcto, el problema es que de esta forma estarias borrando no solamente los archivos de
cara en el directorio actual, sino también de todos los otros sub-directorios "colgados" en éste. Veamos entonces como hacer:
ls -l | grep " cara " | cut -c55- | xargs rm
De esta forma, el
grep selecionó los archivos que contenían la cadena
cara en el directorio actual listado por el
ls -l. El comando
cut agarró solamente el nombre de los archivos, pasándolos para el borrado, a cargo del
rm usando el comando
xargs como puente
El xargs es también una excelente herramienta de creación de
one-liners (
scripts de solamente una línea). Ve éste para listar todos los dueños de archivos (inclusive sus
links) "colgados" en el directorio
/bin y sus sub-directorios.
$ find /bin -type f -follow | \
xargs ls -al | tr -s ' ' | cut -f3 -d' ' | sort -u
Muchas veces el
/bin es un
link (si no estoy equivocado, en Solaris es) y la opción
-follows obliga al
find a seguir el
link. El comando
xargs alimenta el
ls -al y la secuencia de comandos siguiente es para tomar solamente el 3er campo (dueño) y clasificarlo devolviendo solamente una vez cada dueño (opción
-u del comando
sort, que equivale al comando
uniq).
Opciones del xargs
Tu puedes usar las opciones del
xargs para construir comandos extremamente poderosos.
Opción -i
Para ejemplificar esto y comenzar a entender las principales opciones de esta instrucción, vamos a suponer que tenemos que borrar todos los archivos con extensión
.txt en el directorio actual y presentar sus nombres en pantalla. Ve lo que podemos hacer:
$ find . -type f -name "*.txt" | \
xargs -i bash -c "echo borrando {}; rm {}"
La opción
-i del
xargs cambia pares de llaves
({}) por la cadena que está recibiendo a través del pipe
(|). Entonces en este caso las llaves
({}) serán cambiadas por los nombres de los archivos que satisfagam al comando
find.
Opción -n
Mira aqui el pequeño juego que vamos a hacer con el
xargs:
$ ls | xargs echo > arch.ls
$ cat arch.ls
arch.ls arch1 arch2 arch3
$ cat arch.ls | xargs -n1
arch.ls
arch1
arch2
arch3
Cuando mandamos la salida del
ls para el archivo usando el
xargs, comprobamos lo que ya dijimos, o sea, el
xargs manda todo lo que sea posible (lo suficiente para no generar una sobrecarga en la pila) de una vez sola. En seguida, usamos la opción
-n 1 para listar uno por vez. Sólo para estar seguros, ve el ejemplo a seguir, cuando listaremos dos en cada línea:
$ cat arch.ls | xargs -n 2
arch.ls arch1
arch2 arch3
Sin embargo, la línea arriba podría (y debería) ser escrita sin usar el
pipe (|), de la siguiente forma:
$ xargs -n 2 < arch.ls
Opción -p
Otra excelente opción del
xargs es
-p, en la cual el sistema pregunta si tu realmente deseas ejecutar el comando. Digamos que en un directorio tengas archivos con la extensión
.bug y
.ok, los
.bug están con problemas que después de corregidos son grabados como
.ok. Dá una mirada en la lista de este directório:
$ ls dir
arch1.bug
arch1.ok
arch2.bug
arch2.ok
...
arch9.bug
arch9.ok
Para comparar los archivos buenos con los defectuosos, hacemos:
$ ls | xargs -p -n2 diff -c
diff -c arch1.bug arch1.ok ?...y
....
diff -c arch9.bug arch9.ok ?...y
Opción -t
Para finalizar, el
xargs también tenemos la opción
-t, donde va mostrando las instrucciones que montó antes de ejecutarlas. Me gusta mucho esta opción para ayudar a depurar el comando que fuei montado.
Resumo
Entonces podemos resumir el comando de acuerdo con la tabla a seguir:
-t |
Muestra la línea de comando montada antes de ejecutarla |
| Opción |
Acción |
-i |
Substituye el par de llaves ({}) por las cadenas recibidas |
-nNum |
Manda el máximo de parámetros recibidos, hasta el máximo de Num para el comando a ser ejecutado |
-lNum |
Manda el máximo de líneas recibidas, hasta el máximo de Num para el comando a ser ejecutado |
-p |
Muestra la línea de comando montada y pregunta se desea ejecutarla |
Here Strings
Primero um programador con complejo de inferioridad creó el redireccionamiento de entrada y lo representó con un signo de menor
(<) para representar sus sentimento. En seguida, otro sentiéndose todavia peor, creó el
here document representándolo por dos signos de menor
(<<) porque su complejo era mayor. El tercero, pensó: "estes dos no saben lo que es estar por depremido"... Entonces creó el
here strings representádolo por tres signos de menor
(<<<).
Bromas a parte, el
here strings es utilísimo y, no sé porque, es un perfecto desconocido. En la poquísima literatura que hay sobre el tema, se nota que el
here strings es frecuentemente citado como una variante del
here document, teoría con la que discrepo pues su aplicabilidad es totalmente diferente de aquella.
Su sintáxis es simple:
$ comando <<< $cadena
Donde
cadena es expandida y alimenta la entrada primaria (
stdin) de
comando.
Como siempre, vamos directo a los ejemplos de los dos usos más comunes para que ustedes mismos saquen sus conclusiones.
- Uso #1. Substituyendo la tan usada construcción
echo "cadeia" | comando, que obliga a un fork, creando un subshell y aumentando el tiempo de execución.
Exemplos:
$ a="1 2 3"
$ cut -f 2 -d ' ' <<< $a # Normalmente se hace: echo $a | cut -f 2 -d ' '
2
$ echo $Nomearch
Mis Documentos # Arrrghhh!
$ tr "A-Z " "a-z_" <<< $Nomearch # Substituyendo el echo $Nomearch | tr "A-Z " "a-z_"
mis_documentos
$ bc <<<"3 * 2"
6
$ bc <<<"scale = 4; 22 / 7"
3.1428
Para mostrar la mejoría en el desempeño, vamos a hacer un loop de 500 veces usando el ejemplo dado para el comando
tr:
Vea ahora esta secuencia de comandos con medidas de tiempo:
$ time for ((i=1; i<= 500; i++)); { tr "A-Z " "a-z_" <<< $Nomearch >/dev/null; }
real 0m3.508s
user 0m2.400s
sys 0m1.012s
$ time for ((i=1; i<= 500; i++)); { echo $Nomearch | tr "A-Z " "a-z_" >/dev/null; }
real 0m4.144s
user 0m2.684s
sys 0m1.392s
Vea ahora esta otra secuencia de comandos con medidas de tiempo:
$ time for ((i=1;i<=100;i++)); { who | cat > /dev/null; }
real 0m1.435s
user 0m1.000s
sys 0m0.380s
$ time for ((i=1;i<=100;i++)); { cat <(who) > /dev/null; }
real 0m1.552s
user 0m1.052s
sys 0m0.448s
$ time for ((i=1;i<=100;i++)); { cat <<< $(who) > /dev/null; }
real 0m1.514s
user 0m1.056s
sys 0m0.412s
Observando este cuadro verás que en el primero, usamos la forma convencional, en el segundo usamos un
named pipe temporario para ejecutar una substitución de processos y en el tercero usamos
here strings. Notará también que al contrario del ejemplo anterior, aqui el uso de
here strings no fue lo más veloz. Pero note bien que en este último caso el comando
who está siendo ejecutado en un
subshell y eso aumentó el proceso como un todo.
Veamos una forma rápida de inserir una línea como encabezamiento de un archivo:
$ cat num
1 2
3 4
5 6
7 8
9 10
$ cat - num <<< "Impares Pares"
Impares Pares
1 2
3 4
5 6
7 8
9 10
- Uso #2. Otra buena forma de usar el here strings es juntándolo con un comando
read, no perdiendo de vista lo que aprendimos sobre IFS (vea aquí, en la explicación del comando for). El comando cat con las opciones -vet muestra el <ENTER> como $, el <TAB> como ^I y los otros caracteres de control con la notación ^L donde L es una letra cualquiera. Veamos entonces el contenido de una variable y después vamos a leer cada uno de sus campos:
Exemplos:
$ echo "$línea"
Leonardo Mello (21)3313-1329
$ cat -vet <<< "$línea"
Leonardo Mello^I(21)3313-1329$ # Los separadores son blanco y <TAB> (^I)
$ read Nom SNom Tel <<< "$línea"
$ echo "${Nom}_$S{Nom}_$Tel" # Vamos a ver si leyó cada uno de los campos
Leonardo_Mello_(21)3313-1329 # Leyó porque los separadores eran iguales al IFS
También podemos leer directo de un vector (
array) vea:
$ echo $Frutas
Pera:Uva:Manzana
$ IFS=:
$ echo $Frutas
Pera Uva Manzana # Sin las comillas el shell muestra el IFS como blanco
$ echo "$Frutas"
Pera:Uva:Manzana # Ahhh, ahora sí!
$ read -a aFrutas <<< "$Frutas" # La opción -a del read, lee un vector
$ for i in 0 1 2
> do
> echo ${aFrutas[$i]} # Imprimiendo cada elemento del vetor
> done
Pera
Uva
Manzana
Y no te olvides, cualquer duda o falta de compañia para tomar un chopp, lo único que tienes que hacer es mandarme un e-mail para
julio.neves@uniriotec.br para informarse.
Gracias y hasta la próxima!
--
HumbertoPina - 24 Jan 2007

Copyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki-SL?
Send feedback