809 votes

Comment fonctionne "cat << EOF" dans bash?

J'avais besoin d'écrire un script pour entrer une entrée multi-ligne dans un programme (psql).

Après un peu de recherche sur Google, j'ai trouvé que la syntaxe suivante fonctionne :

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Cela construit correctement la chaîne multi-ligne (de BEGIN; à END;, inclusivement) et la passe en tant qu'entrée à psql.

Mais je n'ai aucune idée de comment/pourquoi cela fonctionne, quelqu'un pourrait-il s'il vous plaît expliquer?

Je fais principalement référence à cat << EOF, je sais que > écrit dans un fichier, >> ajoute à un fichier, < lit une entrée depuis un fichier.

Que fait exactement <<?

Et y a-t-il une page de manuel à ce sujet?

34 votes

C'est probablement une utilisation inutile de cat. Essayez psql ... << EOF ... Voir aussi "here strings". mywiki.wooledge.org/BashGuide/InputAndOutput?#Here_Strings

1 votes

Je suis surpris que cela fonctionne avec cat mais pas avec echo. cat devrait s'attendre à un nom de fichier en tant que stdin, pas une chaîne de caractères. psql << EOF semble logique, mais pas autrement. Fonctionne avec cat mais pas avec echo. Comportement étrange. Une idée à ce sujet ?

0 votes

En me répondant : chat sans paramètres exécute et réplique en sortie tout ce qui est envoyé via l'entrée (stdin), utilisant ainsi sa sortie pour remplir le fichier via >. En fait, un nom de fichier lu comme un paramètre n'est pas un flux stdin.

664voto

Vojtech Vitek Points 1465

La syntaxe cat < est très utile lorsque l'on travaille avec du texte sur plusieurs lignes en Bash, par exemple lorsque l'on attribue une chaîne sur plusieurs lignes à une variable shell, un fichier ou un tuyau.


Exemples d'utilisation de la syntaxe `cat < en Bash :`
======================================================
  1. Attribuer une chaîne sur plusieurs lignes à une variable shell

    $ sql=$(cat <



_La variable `$sql` contient maintenant les caractères de retour à la ligne également. Vous pouvez le vérifier avec `echo -e "$sql"`._

2. Passer une chaîne sur plusieurs lignes à un fichier en Bash
--------------------------------------------------------------

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

_Le fichier `print.sh` contient à présent :_

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

3. Passer une chaîne sur plusieurs lignes à un tuyau en Bash
------------------------------------------------------------

    $ cat <

``

_Le fichier `b.txt` contient les lignes `bar` et `baz`. Le même résultat est affiché sur `stdout`._

`` ``` ```` `````

4 votes

1. 1 et 3 peuvent être faits sans chat; 2. L'exemple 1 peut être fait avec une simple chaîne de texte multi-ligne

2 votes

Au lieu de créer un autre processus avec 'cat', pourquoi ne pas utiliser IFS='' read -r -d à la place?

1 votes

Il convient de noter que lorsque vous utilisez tee au lieu de cat, sudo peut être utilisé pour écrire dans un fichier à un emplacement restreint. Par exemple, sudo tee /etc/somepath/file > /dev/null <

619voto

KennyTM Points 232647

Ceci s'appelle le format heredoc _pour fournir une chaîne dans stdin. Voir https://en.wikipedia.org/wiki/Here_document#Unix_shells pour plus de détails._

_

De man bash:

Documents Here

Ce type de redirection indique au shell de lire l'entrée de la source actuelle jusqu'à ce qu'une ligne contenant uniquement le mot (sans espace à la fin) soit vue.

Toutes les lignes lues jusqu'à ce point sont ensuite utilisées comme entrée standard pour une commande.

Le format des here-documents est le suivant :

          <<[-]mot
                  ici-document
          délimiteur

Aucune expansion de paramètres, de substitution de commandes, d'expansion arithmétique, ou d'expansion de chemin d'accès n'est effectuée sur mot. Si des caractères dans mot sont cités, le délimiteur est le résultat de la suppression des guillemets sur mot, et les lignes du ici-document ne sont pas étendues. Si mot n'est pas entre guillemets, toutes les lignes du ici-document sont soumises à l'expansion de paramètres, à la substitution de commandes, et à l'expansion arithmétique. Dans ce dernier cas, la séquence de caractères \ est ignorée, et \ doit être utilisé pour citer les caractères \, $, et `.

Si l'opérateur de redirection est <<-, alors tous les caractères de tabulation initiaux sont supprimés des lignes d'entrée et la ligne contenant le délimiteur. Cela permet aux here-documents à l'intérieur de scripts shell d'être indentés de manière naturelle.

_

16 votes

J'avais beaucoup de mal à désactiver l'expansion de variable/paramètre. Tout ce que je devais faire était d'utiliser des "guillemets doubles" et ça a tout arrangé ! Merci pour l'info !

15 votes

En ce qui concerne <<- veuillez noter que seuls les caractères d'indentation tab sont supprimés, pas les caractères de tabulation souples. C'est l'un de ces rares cas où vous avez en réalité besoin du caractère de tabulation. Si le reste de votre document utilise des tabulations souples, assurez-vous d'afficher les caractères invisibles et (par exemple) copiez-collez un caractère de tabulation. Si vous le faites correctement, votre coloration syntaxique devrait correctement identifier le délimiteur de fin.

1 votes

Je ne vois pas en quoi cette réponse est plus utile que celles ci-dessous. Elle ne fait que régurgiter des informations qui peuvent être trouvées ailleurs (et qui ont probablement déjà été vérifiées)

379voto

edelans Points 609

En votre cas, "EOF" est connu sous le nom de "Balise Here". Fondamentalement, < indique au shell que vous allez entrer une chaîne sur plusieurs lignes jusqu'à la "balise" `Here`. Vous pouvez nommer cette balise comme vous le souhaitez, c'est souvent `EOF` ou `STOP`.

`Quelques règles concernant les balises Here :

  1. La balise peut être n'importe quelle chaîne, en majuscules ou en minuscules, bien que la plupart des gens utilisent souvent des majuscules par convention.
  2. La balise ne sera pas considérée comme une balise Here s'il y a d'autres mots sur cette ligne. Dans ce cas, elle sera simplement considérée comme faisant partie de la chaîne. La balise doit être seule sur une ligne séparée pour être considérée comme une balise.
  3. La balise ne doit pas avoir d'espaces avant ou après sur cette ligne pour être considérée comme une balise. Sinon, elle sera considérée comme faisant partie de la chaîne.

exemple :

$ cat >> test < Bonjour le monde ICI <-- Pas seul sur une ligne séparée -> pas considéré comme fin de chaîne
> C'est un test
>  ICI <-- Espace au début, donc pas considéré comme fin de chaîne
> et une nouvelle ligne
> ICI <-- Maintenant nous avons la fin de la chaîne`

55 votes

Ceci est la meilleure réponse actuelle ... vous définissez les deux et indiquez clairement l'objectif principal de l'utilisation au lieu de la théorie associée ... ce qui est important mais pas nécessaire ... merci - super utile

5 votes

@edelans vous devez ajouter que lorsque <<- est utilisé, le tabulation initiale ne empêchera pas la balise d'être reconnue

1 votes

Votre réponse m'a dirigé vers "vous allez entrer une chaîne de caractères sur plusieurs lignes"

121voto

Ciro Santilli Points 3341

Les guillemets empêchent l'expansion des paramètres

Sans guillemets:

a=0
cat <

Output:

    0

Avec guillemets:

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

ou (moche mais valide):

    a=0
    cat <

Sortie:

$a

Le tiret enlève les tabulations initiales

Sans tiret:

cat <a
EOF

où est une tabulation littérale, et peut être insérée avec Ctrl + V

Sortie:

a

Avec tiret:

cat <<-EOF
a
EOF

Sortie:

a

Cela existe bien sûr pour pouvoir indenter votre cat comme le code environnant, ce qui est plus facile à lire et à maintenir. Par exemple:

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

Malheureusement, cette méthode ne fonctionne pas avec les espaces : POSIX a favorisé l'indentation avec les tabulations. Oups.

POSIX 7

kennytm a cité man bash, mais la plupart de cela est aussi du POSIX 7: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 :

Les opérateurs de redirection "<<" et "<<-" permettent tous deux de rediriger des lignes contenues dans un fichier d'entrée du shell, appelé "here-document", vers l'entrée d'une commande.

Le here-document doit être traité comme un seul mot qui commence après le prochain et se poursuit jusqu'à ce qu'il y ait une ligne contenant uniquement le délimiteur et un , sans caractères entre les deux. Ensuite, le prochain here-document démarre, s'il y en a un. Le format est le suivant:

[n]<

`

où le n optionnel représente le numéro de descripteur de fichier. Si le numéro est omis, le here-document fait référence à l'entrée standard (descripteur de fichier 0).

Si un caractère de word est entre guillemets, le délimiteur est formé en effectuant la suppression des guillemets sur word, et les lignes du here-document ne seront pas étendues. Sinon, le délimiteur est word lui-même.

Si aucun caractère de word n'est entre guillemets, toutes les lignes du here-document seront étendues pour l'expansion des paramètres, la substitution de commandes et l'expansion arithmétique. Dans ce cas, le backslash dans l'entrée se comporte comme le backslash à l'intérieur des guillemets doubles (voir Guillemets Doubles). Cependant, le caractère de guillemet double ('"') ne sera pas traité de manière spéciale à l'intérieur d'un here-document, sauf lorsque le guillemet double apparaît dans "$()", "``", ou "${}".

Si le symbole de redirection est "<<-", tous les caractères de tabulation initiaux seront supprimés des lignes d'entrée et de la ligne contenant le délimiteur final. Si plus d'un opérateur "<<" ou "<<-" est spécifié sur une ligne, le here-document associé au premier opérateur sera fourni en premier par l'application et sera lu en premier par le shell.

Lorsqu'un here-document est lu à partir d'un terminal et que le shell est interactif, il écrira le contenu de la variable PS2, traité comme décrit dans les Variables du Shell, sur la sortie standard d'erreur avant de lire chaque ligne d'entrée jusqu'à ce que le délimiteur soit reconnu.

`

``` ````

0 votes

Dans votre dernier exemple discutant de <<- et a, il convient de noter que le but était de permettre une indentation normale du code dans le script tout en permettant au texte heredoc présenté au processus récepteur de commencer en colonne 0. Il s'agit d'une fonctionnalité peu commune et un peu plus de contexte pourrait éviter beaucoup de perplexité...

1 votes

Comment devrais-je échapper à l'expansion si une partie du contenu entre mes balises EOF a besoin d'être étendue et certaines non ?

2 votes

...il suffit d'utiliser le backslash devant le $

31voto

Andreas Maier Points 116

Utilisation de tee au lieu de cat

Pas exactement en réponse à la question initiale, mais je voulais partager cela de toute façon : j'ai eu besoin de créer un fichier de configuration dans un répertoire nécessitant des droits root.

La méthode suivante ne fonctionne pas pour ce cas :

$ sudo cat </etc/somedir/foo.conf
# mon fichier de configuration
foo=bar
EOF

car la redirection est gérée en dehors du contexte sudo.

J'ai fini par utiliser ceci à la place :

$ sudo tee </dev/null
# mon fichier de configuration
foo=bar
EOF

1 votes

Dans votre cas, utilisez sudo bash -c 'cat </etc/somedir/foo.conf # mon fichier de configuration foo=bar EOF'

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X