Difference: TWikiBarbirinaites_esp ( vs. 1)

Revision 124 Jan 2007 - Main.HumbertoPina

Line: 1 to 1
Added:
>
>

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.

Pinguim com placa de atenção 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

 
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Wiki-SL? Send feedback