¿Cómo funciona "cat

Necesitaba escribir un script para introducir entradas multilíneas a un programa (psql).

Después de buscar un poco en Google, encontré que la siguiente sintaxis funciona:

cat << EOF | psql ---params
BEGIN;

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Esto construye correctamente la cadena multilínea (de BEGIN; a END;, inclusive) y la canaliza como entrada a psql.

Pero no tengo idea de cómo/por qué funciona, ¿puede alguien por favor explicar?

Me refiero principalmente a cat << EOF, sé > salidas a un archivo, >> anexa a un archivo, < lee la entrada de file.

¿Qué hace << exactamente?

¿Y hay una página de manual para ello?

Author: Benjamin W., 2010-03-23

7 answers

Esto se llama heredoc formato para proporcionar una cadena en stdin. Véase https://en.wikipedia.org/wiki/Here_document#Unix_shells para más detalles.


De man bash:

Aquí Documentos

Este tipo de redirección le indica al shell que lea la entrada desde la fuente actual hasta una línea conteniendo solo palabra (sin final espacios en blanco).

Todas las líneas leídas hasta ese punto se utilizan entonces como el entrada estándar para un comando.

El formato de here-documents es:

          <<[-]word
                  here-document
          delimiter

No hay expansión de parámetros, sustitución de comandos, expansión aritmética, o la expansión pathname se realiza en palabra. Si algún carácter en word es citado, el delimiter es el resultado de la eliminación de comillas en word , y las líneas en el aquí-documento no se expanden. Si la palabra no está entrecomillada, todas las líneas de la aquí-documento están sujetos a la expansión de parámetros, comando sustitución y aritmética ampliación. En este último caso, la la secuencia de caracteres \<newline> es ignorado, y \ debe usarse para citar los caracteres \, $, y `.

Si el operador de redirección es <<-, entonces todos los caracteres de tabulación iniciales se despojan de las líneas de entrada y el línea que contiene delimitador. Este permite aquí-documentos dentro de scripts de shell para ser sangrados en un moda natural.

 376
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-26 04:53:16

La sintaxis cat <<EOF es muy útil cuando se trabaja con texto multilínea en Bash, por ejemplo. cuando se asigna una cadena multilínea a una variable de shell, un archivo o una tubería.

Ejemplos de cat <<EOF uso de sintaxis en Bash:

1. Asignar cadena multilínea a una variable de shell

$ sql=$(cat <<EOF
SELECT foo, bar FROM db
WHERE foo='baz'
EOF
)

La variable $sql ahora también contiene los caracteres de nueva línea. Puede verificar con echo -e "$sql".

2. Pasar cadena multilínea a un archivo en Bash

$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF

El archivo print.sh ahora contiene:

#!/bin/bash
echo $PWD
echo /home/user

3. Pasar cadena multilínea a una tubería en Bash

$ cat <<EOF | grep 'b' | tee b.txt
foo
bar
baz
EOF

El archivo b.txt contiene las líneas bar y baz. La misma salida se imprime en stdout.

 343
Author: Vojtech Vitek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-14 22:01:53

En su caso, "EOF" se conoce como una "Etiqueta Here". Básicamente <<Here le dice al shell que va a introducir una cadena multilínea hasta la "etiqueta" Here. Puedes nombrar esta etiqueta como quieras, a menudo es EOF o STOP.

Algunas reglas sobre las etiquetas Aquí:

  1. La etiqueta puede ser cualquier cadena, mayúscula o minúscula, aunque la mayoría de la gente usa mayúscula por convención.
  2. La etiqueta no será considerada como una etiqueta Here si hay otras palabras en esa línea. En este caso, simplemente ser considerado parte de la cadena. La etiqueta debe estar por sí misma en una línea separada, para ser considerada una etiqueta.
  3. La etiqueta no debe tener espacios iniciales o finales en esa línea para ser considerada una etiqueta. De lo contrario se considerará como parte de la cadena.

Ejemplo:

$ cat >> test <<HERE
> Hello world HERE <-- Not by itself on a separate line -> not considered end of string
> This is a test
>  HERE <-- Leading space, so not considered end of string
> and a new line
> HERE <-- Now we have the end of the string
 157
Author: edelans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 09:31:14

POSIX 7

Kennytm citó man bash, pero la mayor parte de eso también es POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

Los operadores de redirección "

El documento aquí se tratará como una sola palabra que comienza después de la siguiente y continúa hasta que haya una línea que contiene solo el delimitador y una, sin caracteres entre ellos. Entonces comienza el siguiente documento aquí, si hay uno. El formato es el siguiente:

[n]<<word
    here-document
delimiter

Donde la n opcional representa el número del descriptor de fichero. Si se omite el número, el documento here se refiere a la entrada estándar (descriptor de archivo 0).

Si se cita algún carácter en word, el delimitador se formará mediante la eliminación de comillas en word, y las líneas aquí-documento no expandirse. De lo contrario, el delimitador será la palabra misma.

Si no se citan caracteres en word, todas las líneas del documento aquí se expandirán para la expansión de parámetros, la sustitución de comandos y la expansión aritmética. En este caso, en la entrada se comporta como las comillas dobles interiores (consulte Comillas dobles). Sin embargo, el carácter de comilla doble ( '"' ) no se tratará especialmente dentro de un documento aquí, excepto cuando la comilla doble aparezca dentro "$()", "`", o "${}".

Si el símbolo de redirección es "<tab> se eliminarán de las líneas de entrada y de la línea que contiene el delimitador final. Si se especifica más de un operador "

Cuando se lee un documento aquí desde un dispositivo terminal y el shell es interactivo, debe escribir el contenido de la variable PS2, procesada como se describe en Variables de Shell, a error estándar antes de leer cada línea de entrada hasta que el delimitador haya sido reconocido.

Ejemplos

Algunos ejemplos aún no dados.

Las comillas impiden la expansión de parámetros

Sin comillas:

a=0
cat <<EOF
$a
EOF

Salida:

0

Con comillas:

a=0
cat <<'EOF'
$a
EOF

O (feo pero válido):

a=0
cat <<E"O"F
$a
EOF

Salidas:

$a

El guion elimina el interlineado tabs

Sin guion:

cat <<EOF
<tab>a
EOF

Donde <tab> es una pestaña literal, y se puede insertar con Ctrl + V <tab>

Salida:

<tab>a

Con guion:

cat <<-EOF
<tab>a
<tab>EOF

Salida:

a

Esto existe, por supuesto, para que pueda sangrar su cat como el código circundante, que es más fácil de leer y mantener. Por ejemplo:

if true; then
    cat <<-EOF
    a
    EOF
fi

Desafortunadamente, esto no funciona para caracteres de espacio: POSIX favoreció la sangría tab aquí. Uy.

 47
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-25 14:51:36

Usando tee en lugar de cat

No exactamente como una respuesta a la pregunta original, pero quería compartir esto de todos modos: Tenía la necesidad de crear un archivo de configuración en un directorio que requería derechos de root.

Lo siguiente no funciona para ese caso:

$ sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
foo=bar
EOF

Porque la redirección se maneja fuera del contexto sudo.

Terminé usando esto en su lugar:

$ sudo tee <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
 16
Author: Andreas Maier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-13 12:14:06

Esto no es necesariamente una respuesta a la pregunta original, sino un intercambio de algunos resultados de mis propias pruebas. Esto:

<<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

Producirá el mismo archivo que:

cat <<test > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
test

Entonces, no veo el punto de usar el comando cat.

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-06 00:15:40

Vale la pena señalar que aquí los documentos también funcionan en bucles bash. Este ejemplo muestra cómo obtener la lista de columnas de la tabla:

export postgres_db_name='my_db'
export table_name='my_table_name'

# start copy 
while read -r c; do test -z "$c" || echo $table_name.$c , ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
SELECT column_name
FROM information_schema.columns
WHERE 1=1
AND table_schema = 'public'
AND table_name   =:'table_name'  ;
EOF
)
# stop copy , now paste straight into the bash shell ...

output: 
my_table_name.guid ,
my_table_name.id ,
my_table_name.level ,
my_table_name.seq ,

O incluso sin la nueva línea

while read -r c; do test -z "$c" || echo $table_name.$c , | perl -ne 
's/\n//gm;print' ; done < <(cat << EOF | psql -t -q -d $postgres_db_name -v table_name="${table_name:-}"
 SELECT column_name
 FROM information_schema.columns
 WHERE 1=1
 AND table_schema = 'public'
 AND table_name   =:'table_name'  ;
 EOF
 )

 # output: daily_issues.guid ,daily_issues.id ,daily_issues.level ,daily_issues.seq ,daily_issues.prio ,daily_issues.weight ,daily_issues.status ,daily_issues.category ,daily_issues.name ,daily_issues.description ,daily_issues.type ,daily_issues.owner
 0
Author: Yordan Georgiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-14 15:18:35