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?
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.
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.
cat <<EOF
dans Bash:$ 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"
.
$ cat <<EOF > print.sh
#!/bin/bash
echo \$PWD
echo $PWD
EOF
Le fichier print.sh
contient maintenant:
#!/bin/bash
echo $PWD
echo /home/user
$ 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
.
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:
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
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.
Quelques exemples non encore donnés.
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
Sans trait d'union:
cat <<EOF
<tab>a
EOF
où <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
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
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.