web-dev-qa-db-fra.com

Comment fonctionne "chat << EOF" à Bash?

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

Après un peu de googler, j'ai trouvé la syntaxe suivante fonctionne:

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

`pg_dump ----something`

update table .... statement ...;

END;
EOF

Cela construit correctement la chaîne multiligne (de BEGIN; à END;, inclus) et la renvoie comme une entrée à psql.

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

Je fais principalement référence à cat << EOF, je sais que > est envoyé dans un fichier, >> est ajouté à un fichier, < lit les entrées à partir d'un fichier. 

Que fait exactement <<?

Et y at-il une page de manuel pour cela?

467
hasen

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


De man bash:

Ici les documents

Ce type de redirection demande au shell de lire les entrées à partir de la source actuelle jusqu'à une ligne contenant uniquement Word (sans espaces blancs suivis) est vu.

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

Le format de here-documents est:

          <<[-]Word
                  here-document
          delimiter

Aucune extension de paramètre, substitution de commande, extension arithmétique ou l'extension du chemin est effectuée sur Word. Si des caractères dans Word sont cité, le délimiteur est le résultat de la suppression des guillemets sur Word et des lignes dans le here-document ne sont pas développés . Si Word n'est pas entre guillemets, toutes les lignes du fichier here-document sont soumis à une extension de paramètre, commande substitution et arithmétique expansion. Dans ce dernier cas, le la séquence de caractères \<newline> est ignored, et \ doit être utilisé pour citer les caractères \, $ et `.

Si l'opérateur de redirection est <<-, tous les caractères de tabulation en tête sont supprimés des lignes d’entrée et du ligne contenant délimiteur. Ce permet à indentation naturelle des documents ici dans les scripts Shell.

417
kennytm

La syntaxe cat <<EOF est très utile lorsque vous travaillez avec du texte multiligne dans Bash, par exemple. lors de l’affectation de chaînes multilignes à une variable, un fichier ou un canal Shell.

Exemples d'utilisation de la syntaxe cat <<EOF dans Bash:

1. Affecter une chaîne multiligne à une variable Shell

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

La variable $sql contient maintenant les caractères de la nouvelle ligne. Vous pouvez vérifier avec echo -e "$sql".

2. Passer une chaîne multiligne à un fichier dans Bash

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

Le fichier print.sh contient maintenant:

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

3. Passez une chaîne multiligne à un tuyau dans Bash

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

Le fichier b.txt contient les lignes bar et baz. La même sortie est imprimée à stdout.

380
Vojtech Vitek

Dans votre cas, "EOF" est appelé "Here Tag". <<Here indique au shell que vous allez entrer une chaîne multiligne jusqu'à la "balise" Here. Vous pouvez nommer cette balise comme bon vous semble, il s’agit souvent de EOF ou STOP.

Quelques règles sur les balises Here:

  1. La balise peut être n’importe quelle chaîne, majuscule ou minuscule, bien que la plupart des gens l’utilisent par convention.
  2. La balise ne sera pas considérée comme une balise Here s'il y a d'autres mots dans cette ligne. Dans ce cas, il sera simplement considéré comme faisant partie de la chaîne. La balise doit être seule sur une ligne distincte, pour être considérée comme une balise.
  3. La balise ne doit pas comporter d'espaces de début ni de fin dans cette ligne pour être considérée comme une balise. Sinon, il sera considéré comme faisant partie de la chaîne.

exemple: 

$ 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
179
edelans

POSIX 7

kennytm a cité man bash, mais il s’agit principalement de 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 les lignes contenues dans un fichier d'entrée Shell, appelé "here-document", vers l'entrée d'une commande.

Le document ici doit être traité comme un seul mot qui commence après le suivant et continue jusqu'à ce qu'il y ait une ligne contenant uniquement le délimiteur et un, sans caractères entre ceux-ci. Ensuite, le document suivant commence, s'il en existe un. Le format est le suivant:

[n]<<Word
    here-document
delimiter

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

Si un caractère est cité dans Word, le délimiteur doit être formé en supprimant les guillemets sur Word et les lignes du document ne doivent pas être développées. Sinon, le délimiteur sera la Parole elle-même.

Si aucun caractère dans Word n'est cité, toutes les lignes du document here doivent être développées pour le développement des paramètres, la substitution de commandes et le développement arithmétique. Dans ce cas, l'entrée dans se comporte comme les guillemets intérieurs (voir Double-guillemets). Toutefois, le caractère guillemet double ('"') ne doit pas être traité spécialement dans un document here, sauf lorsque la guillemet double apparaît entre" $ () "," `` "ou" $ {} ".

Si le symbole de réorientation est "<< -", tous les premiers caractères <tab> doivent être supprimés des lignes d'entrée et de la ligne contenant le délimiteur de fin. Si plusieurs opérateurs "<<" ou "<< -" sont spécifiés sur une ligne, le document ici associé au premier opérateur doit être fourni en premier par l'application et doit être lu en premier par le shell.

Lorsqu'un document ici est lu à partir d'un terminal et que le shell est interactif, il doit écrire le contenu de la variable PS2, traitée comme décrit dans Variables du shell, dans l'erreur standard avant de lire chaque ligne d'entrée jusqu'à ce que le délimiteur soit reconnu.

Exemples

Quelques exemples non encore donnés.

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

Sans citations:

a=0
cat <<EOF
$a
EOF

Sortie:

0

Avec des citations:

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

ou (moche mais valide):

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

Les sorties:

$a

Le trait d'union supprime les onglets en tête

Sans trait d'union:

cat <<EOF
<tab>a
EOF

<tab> est un onglet littéral et peut être inséré avec Ctrl + V <tab>

Sortie:

<tab>a

Avec trait d'union:

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

Sortie:

a

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

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

Malheureusement, cela ne fonctionne pas pour les caractères d'espacement: POSIX a préféré tab indentation ici. Beurk.

Utiliser le tee au lieu du chat

Pas exactement comme réponse à la question initiale, mais je voulais quand même le partager: j'avais besoin de créer un fichier de configuration dans un répertoire qui nécessitait des droits root.

Ce qui suit ne fonctionne pas pour ce cas:

$ Sudo cat <<EOF >/etc/somedir/foo.conf
# my config file
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 <<EOF /etc/somedir/foo.conf >/dev/null
# my config file
foo=bar
EOF
19
Andreas Maier

Il est à noter que les documents ici fonctionnent également dans les boucles Bash. Cet exemple montre comment obtenir la liste des colonnes de la table: 

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 ,

ou même sans la nouvelle ligne 

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
Yordan Georgiev

Ce n'est pas nécessairement une réponse à la question initiale, mais un partage de certains résultats de mes propres tests. Ce:

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

produira le même fichier que:

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

Donc, je ne vois pas l'intérêt d'utiliser la commande cat.

0
user9048395