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

22 diciembre 2016 at 13:16 by Adrián Pérez

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

Otro ejemplo, quizá más interesante, es el de interactuar desde un script bash via telnet contra un mail server, o contra un servidor FTP pasándole en cada línea el comando a ejecutar. Copio el ejemplo directamente de The Linux Document Project:

ftp -n $Server <<End-Of-Session
# -n option disables auto-logon
user anonymous "$Password"       #  If this doesn't work, then try:
                                 #  quote user anonymous "$Password"
binary
bell                             # Ring 'bell' after each file transfer.
cd $Directory
put "$Filename.lsm"
put "$Filename.tar.gz"
bye
End-Of-Session

Hay muchas otras situaciones en las que un here document puede ser útil, así que mírate la documentación.

<<< (Here strings)

Un here string consiste en un COMMAND <<<$WORD, donde $WORD se pasa expandido como stdin a COMMAND.

[adri@adri ~]$ wc -w <<< "voy a contar el número de palabras"
7

Copio otro ejemplo de la documentación:

String="This is a string of words."
 
read -r -a Words <<< "$String"
#  The -a option to "read"
#+ assigns the resulting values to successive members of an array.
 
echo "First word in String is:    ${Words[0]}"   # This
echo "Second word in String is:   ${Words[1]}"   # is
echo "Third word in String is:    ${Words[2]}"   # a
echo "Fourth word in String is:   ${Words[3]}"   # string
echo "Fifth word in String is:    ${Words[4]}"   # of
echo "Sixth word in String is:    ${Words[5]}"   # words.
echo "Seventh word in String is:  ${Words[6]}"   # (null)
                                                 # Past end of $String.

Los here strings resultan especialmente interesantes cuando se combinan con el comando "read", el cual puede hacer uso de la variable especial "IFS" como delimitador. Todo junto, nos permite escribir código como el siguiente:

#!/usr/bin/env bash
 
# Definimos nuestro string de datos
DATA="1a=1b=1c;2a=2b=2c;3a=3b=3c;4a=4b=4c"
IFS=';'
# Partimos el string por ";" y guardamos las partes en el array ADDR 
read -ra ADDR <<< "$DATA"
IFS="="
for i in "${ADDR[@]}";
do
        echo "i: $i"
        # Para cada elemento de ADDR volvemos a separar esta vez por "="
        read primero segundo tercero <<< "$i"
        echo "Columna 1:$primero | Columna 2:$segundo | Columna 3:$tercero"
done

La verdad es que me encontré con un código como el anterior revisando unos scripts, y me pareció suficientemente interesante como para escribir un pequeño post sobre ello, pues no suelo usar here documents o here strings en mis scripts y va bien tenerlos presentes. En fín, el código anterior, si se ejecuta, nos dará como resultado lo siguiente:

[adri@adri ~]$ ./a 
i: 1a=1b=1c
Columna 1:1a | Columna 2:1b | Columna 3:1c
i: 2a=2b=2c
Columna 1:2a | Columna 2:2b | Columna 3:2c
i: 3a=3b=3c
Columna 1:3a | Columna 2:3b | Columna 3:3c
i: 4a=4b=4c
Columna 1:4a | Columna 2:4b | Columna 3:4c

Fuentes:
http://superuser.com/questions/342982/is-there-a-binary-safe-triple-less-than-operator-in-bash#343056
http://tldp.org/LDP/abs/html/here-docs.html
http://www.tldp.org/LDP/abs/html/x17837.html