Here documents (<<), Here strings (<<<) y el comando "read"

bash

<< (Here documents)

Un here document es un tipo de redirección que le dice al shell que vaya leyendo el input que se le está pasando hasta encontrar una determinada palabra clave (‘TAG’ en el ejemplo).

cat >> file << 'TAG'
primera línea
segunda línea
última línea
TAG

Consiste, pues, en un bloque de código cuyas líneas se pasan una a una a un comando o programa interactivo (como ftp o cat). Podemos usar cualquier TAG para indicar el final del bloque que compone el here document, pese a que se suele usar "EOF", pero podríamos usar cualquier cosa, mientras estemos seguros de que será una cadena de carácteres que no se usará dentro del bloque que queremos procesar.

Un posible uso es el de añadir un bloque de líneas a un fichero. Cabe decir que el here document incluirá los espacios/tabs de inicio de cada línea. Si no es lo que quieres, puedes eliminar los tabs de inicio de línea al procesar el here document añadiéndo un "-" justo tras la redirección y antes del tag (quedaría "<<-"). Aquí un ejemplo manteniendo tabs y usando variables dentro del bloque:

NOMBRE="$1"
cat >> file << EOF
Hola $NOMBRE
este es mi bloque de líneas
que guardaré en file
        esta línea tiene un tab 
  y esta dos espacios
EOF

Continuar leyendo "Here documents (<<), Here strings (<<<) y el comando "read""

Bash bang commands y más

bash
A continuación una serie de comandos útiles en bash, que (por lo menos a mí) me viene bien tener a mano, razón por la cual seguramente este post se actualice varias veces a medida que vaya añadiéndo comandos útiles a mi día a día.

«$?»: Exit code

«$?» Muestra el exit code del comando que se acaba de ejecutar.

[adri@localhost tmp]$ cat test 
#!/bin/bash
exit 0
[adri@localhost tmp]$ ./test 
[adri@localhost tmp]$ echo $?
0
[adri@localhost tmp]$ cat test 
#!/bin/bash
exit 1
[adri@localhost tmp]$ ./test 
[adri@localhost tmp]$ echo $?
1

Ésto se puede usar dentro de scripts en bash, para saber cómo ha acabado la línea anterior del script, lo cual puede resultar muy muy útil.

Continuar leyendo «Bash bang commands y más»

Algunos apuntes sobre Bash

bashHe querido recoger algunas notas de Bash scripting para no tener que correr a Google (o a cualquier página de StackExchange) cada vez que las dudas me asalten.

/usr/bin/env bash vs /usr/bin/bash

Leí en la siguiente página sobre las «mejores prácticas en Bash» acerca del uso de «#!/usr/bin/env bash» en lugar del habitual «#!/usr/bin/bash» o «#!/bin/bash«. La idea es usar»#!/usr/bin/env bash» pues de esta manera lo que hacemos es buscar en el PATH dónde está bash, el cual es probable se encuentre en ubicaciones distintas en servidores diferentes. Ésto evitará problemas portando scripts a otros entornos. [Más info] [2]

#!/usr/bin/env bash

Corchete simple vs Doble corchete

Básicamente, ambos sirven para evaluar expresiones. «[» es un alias para el comando «test» y tanto uno como el otro están disponibles en el estándar POSIX. «[[» sin embargo es una keyword, no un programa, y se trata una versión mejorada para evaluar expresiones, únicamente disponible en Bash, KornShell y Zsh. Si no estás preocupado por la portabilidad de tus scripts con sistemas donde no cuentes con Bash, es mejor usar siempre el doble corchete, por la funcionalidad extra que aporta (posibilidad de usar && y ||, expresiones regulares, pattern matching, etc.). [Más info] [2]

La siguiente evaluación será «true» pues b és mayor que a, 8 mayor que 4, y abcd entra dentro del patrón «a*»:

if [[ b > a && 8 -gt 4 && abcd = a* ]]; then ...

PD: Dejo un enlace a la wikipedia donde se especifican los nombres en inglés de éstos símbolos, según la zona.

Continuar leyendo «Algunos apuntes sobre Bash»

Monitorizar upload FTP desde script

Upload FTP¿Cómo podríamos saber si un fichero que está siendo subido por FTP ha acabado o no de subirse? Hay algunas posibles soluciones en esta conversación de SuperUser.com, la última de las cuales, es la que he usado.

Para los testeos, he usado un servidor vsFTPd (configurado tal y como expliqué aquí) sobre un servidor CentOS 5.4. Como no tenía ficheros grandes para hacer el test, he creado uno de forma instantánea, con el siguiente comando:

[root@adripc]# fallocate -l 100M test.img
[root@adripc]# ls -lah test.img
-rw-r–r– 1 root root 100M Jul 18 10:20 test.img

La idea es usar lsof para saber el PID del proceso de vsFTPd que está siendo usado para subir el fichero destino, y entoces monitorizar ese PID.  El problema que me he encontrado, al menos con el entorno de test que he usado, es que ese PID de subida puede cambiar durante la subida. No sé bien bien porqué razón, pero en mi caso cambia. Si cambia el PID entonces ya no podremos monitorizar ese PID, ya que el PID habrá finalizado (o no) pero no así la subida, que estará usando otro PID diferente.

En este caso, la solución es más sencilla aun, ya que bastará con símplemente ir consultando mediante lsof y en caso de devolver vacío, nos indicará que no hay ningún proceso usando el fichero y que por tanto, la subida ha finalizado. Así, nos dará igual que el PID cambie. Lo único importante es que durante la subida del fichero, ese archivo tendrá un PID asociado, mientras que cuando haya acabado el upload, no tendrá ningún PID y por tanto lsof devolverá vacío.

Continuar leyendo «Monitorizar upload FTP desde script»

Copia con exclusiones

Imaginemos que tenemos un directorio con decenas de miles de ficheros. Imaginemos ahora que necesitamos copiarlos casi todos, a otro directorio. ¿Cómo conseguimos, mediante un único comando, realizar la copia de todos los ficheros excepto los que no necesitamos copiar?

Una solución sería, estando ubicados en el directorio de origen (que contiene los ficheros) con la siguiente línea:

ls | egrep -v ‘^excepcion1|^excepcion2|^excepcion3’ | xargs -i cp {} /dest_folder/

En esta línea, excepcion1 corresponde con el nombre de un fichero que no queremos copiar (o en el caso de tener varios ficheros cuyo nombre empieza por excepcion1, con todos ellos). Análogamente con excepcion2 y excepcion3. dest_folder, corresponde con el directorio de destino.

Por ejemplo, si se quisieran copiar todos los archivos, excepto los que empiezan por adoc*, data*, table* y user*, se podría usar la siguiente sentencia:

ls | egrep -v ‘^adoc|^data|^table|^user’ | xargs -i cp {} /dest_folder/

NOTA: con ‘^’ se indica que el nombre del archivo (en este caso) ha de empezar con el patrón indicado.