27 July 2013

Lazos... en shell

Hace poco tuve que hacer unas modificaciones a varios archivos. Lo ideal habría sido crear un programita (en Delphi/Lazarus o en Python) y resolver el problema. Pero la verdad a veces con una simple instrucción en un shell se soluciona todo. Tuve que hacerlo en Windows, pero obviamente también se puede en Linux, aunque la sintaxis es diferente.

Veamos los requerimientos y cómo solucionarlos.

Primero vamos a copiar varios archivos de un lugar a otro. Se sabe el nombre del archivo, pero una copia con comodín en la extensión no va a servir pues no todas las extensiones se permiten. Digamos que los archivos a copiar llevan por nombre

Azul, Verde, Amarillo, Rojo, Naranja, Gris, Blanco y Negro

y las extensiones válidas son

txt, pas, py, pyc y gif

Pero existen también extensiones bak, ~old, dat, tmp y otras. Y esas no nos interesan. Por eso una copia con comodín no sirve. Sin embargo, es fácil con un lazo for. Si estamos en Windows la forma es:

for %%1 in (<lista de nombres>) do for %%2 in (<lista de extensiones>) do copy %%1.%%2 <dest>


Se usa un doble % si el comando está dentro de un .bat o .cmd (bat y cmd son lo mismo para el shell) y si lo ejecutamos directamente en una ventana cmd.exe entonces se usa un solo %

En nuestro caso el comando quedaría así:

for %%1 in (Azul, Verde, Amarillo, Rojo, Naranja, Gris, Blanco, Negro) do for %%2 in (txt, pas, py, pyc, gif) do copy F:\RutaFuente\%%1.%%2 G:\RutaDestino

Eso funciona bien porque ya sabíamos de antemano los nombres de los archivos a procesar, pero qué pasa si éste no es el caso? Supongamos que tenemos que eliminar todos los archivos '*.~bak' que haya en una determinada ruta recursivamente. Aún podemos seguir usando un lazo for:

for /f in "tokens=*" %%1 in ('dir H:\Ruta\*.~bak /s /b /n') do del /q "%%1"

El parámetro /f le dice al for que procese en determinada manera los argumentos in (..) del comando. Admite unas cuantas opciones interesantes, pero para este post con esta opción basta. El dir simplemente lista recursivamente los archivos a partir de la ruta dada; las opciones /b y /n son para simplificar la salida y que el lazo trabaje bien. El del al final simplemente borra sin preguntar (/q) lo que le envíe el for. Se usan comillas para encerrar al parámetro para contemplar nombres de rutas con espacios en ellas. Por la forma en que está hecho se reportarán intentos de borrado de archivos que ya fueron borrados, pero a fin de mantener la simplicidad no corregí este comportamiento. Igual cumple su cometido.

Para más información del for en Windows pueden consultar el help o esta página que hallé con I'm Felling Lucky en Google:


Muy simple. Ahora veamos cómo es en Linux. Los for del shell en Linux son mucho más complejos. En general el shell lo es. Uno puede escribir programas enteros e interactivos que corren como un script en algún shell de Linux (hay varios) Yo uso bash y mis ejemplos funcionarán con él.


for i in Azul Verde Amarillo Rojo Naranja Gris Blanco Negro; do for j in txt pas py pyc gif; do cp "/Source/$i.$j /Dest"; done; done

Los lazos acá son más parecidos a los de los lenguajes estructurados y eso se debe a que el shell lo es! Incluso se pueden crear funciones dentro de un shell!!!

Ahora veamos cómo sería la parte de borrar algo:

for i in /Path/*.~bak; do rm -f "$i"; done

Aunque para esto es innecesario ya que el comando rm puede borrar de forma recursiva. Pero sirve para comparar cómo operan ambas modalidades. Para saber más del uso del for pueden invocar man bash y leer la documentación.

No comments :

Post a Comment

Comparte tu código...