web-dev-qa-db-fra.com

Quelle est la différence entre <<, <<< et <<in bash?

Quelle est la différence entre <<, <<< et < < in bash?

89
Searene

Voici le document

<< est connue sous le nom de here-document structure. Vous laissez le programme savoir quel sera le texte de fin et, chaque fois que ce séparateur sera vu, le programme lira tous les éléments que vous lui avez donnés en entrée et effectuera une tâche.

Voici ce que je veux dire:

$ wc << EOF
> one two three
> four five
> EOF
 2  5 24

Dans cet exemple, nous indiquons au programme wc d'attendre la chaîne EOF, saisissons ensuite cinq mots, puis tapez EOF pour signaler que la saisie est terminée. En fait, cela ressemble à exécuter wc lui-même, taper des mots, puis appuyer sur CtrlD

En bash, ceux-ci sont implémentés via des fichiers temporaires, généralement sous la forme /tmp/sh-thd.<random string>, tandis qu'en tirets, ils sont implémentés en tant que canaux anonymes. Ceci peut être observé via les appels système de traçage avec la commande strace. Remplacez bash par sh pour voir comment /bin/sh effectue cette redirection.

$ strace -e open,dup2,pipe,write -f bash -c 'cat <<EOF
> test
> EOF'

Ici chaîne

<<< est appelé here-string. Au lieu de taper du texte, vous donnez une chaîne de texte prédéfinie à un programme. Par exemple, avec un programme tel que bc, nous pouvons utiliser bc <<< 5*4 pour obtenir simplement une sortie pour ce cas spécifique, il n'est pas nécessaire d'exécuter bc de manière interactive.

Les chaînes présentes dans bash sont implémentées via des fichiers temporaires, généralement au format /tmp/sh-thd.<random string>, qui sont ensuite dissociés, leur permettant ainsi d’occuper temporairement de l’espace mémoire, mais ne figurant pas dans la liste des entrées du répertoire /tmp, et existent en tant que fichiers anonymes. peut toujours être référencé via le descripteur de fichier par le shell lui-même, et ce descripteur de fichier étant hérité par la commande puis dupliqué sur le descripteur de fichier 0 (stdin) via la fonction dup2(). Ceci peut être observé via

$ ls -l /proc/self/fd/ <<< "TEST"
total 0
lr-x------ 1 user1 user1 64 Aug 20 13:43 0 -> /tmp/sh-thd.761Lj9 (deleted)
lrwx------ 1 user1 user1 64 Aug 20 13:43 1 -> /dev/pts/4
lrwx------ 1 user1 user1 64 Aug 20 13:43 2 -> /dev/pts/4
lr-x------ 1 user1 user1 64 Aug 20 13:43 3 -> /proc/10068/fd

Et via le suivi des appels système (sortie abrégée pour des raisons de lisibilité; notez comment le fichier temporaire est ouvert en tant que fd 3, les données y sont écrites, puis il est rouvert avec l'indicateur O_RDONLY en tant que fd 4 et plus tard sans lien, puis dup2() sur fd 0, qui est hérité par cat plus tard):

$ strace -f -e open,read,write,dup2,unlink,execve bash -c 'cat <<< "TEST"'
execve("/bin/bash", ["bash", "-c", "cat <<< \"TEST\""], [/* 47 vars */]) = 0
...
strace: Process 10229 attached
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDWR|O_CREAT|O_EXCL, 0600) = 3
[pid 10229] write(3, "TEST", 4)         = 4
[pid 10229] write(3, "\n", 1)           = 1
[pid 10229] open("/tmp/sh-thd.uhpSrD", O_RDONLY) = 4
[pid 10229] unlink("/tmp/sh-thd.uhpSrD") = 0
[pid 10229] dup2(4, 0)                  = 0
[pid 10229] execve("/bin/cat", ["cat"], [/* 47 vars */]) = 0
...
[pid 10229] read(0, "TEST\n", 131072)   = 5
[pid 10229] write(1, "TEST\n", 5TEST
)       = 5
[pid 10229] read(0, "", 131072)         = 0
[pid 10229] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=10229, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
+++ exited with 0 +++

Opinion: potentiellement parce qu'ici les chaînes utilisent des fichiers texte temporaires, c'est la raison pour laquelle ici-les chaînes insèrent toujours une nouvelle ligne de fin, puisque le fichier texte de définition POSIX doit avoir des lignes qui se terminent par un caractère de nouvelle ligne .

Processus de substitution

Comme tldp.org explique,

La substitution de processus alimente la sortie d'un processus (ou de processus) dans le stdin d'un autre processus.

Donc, en réalité, cela ressemble à la tuyauterie stdout d’une commande à l’autre, par exemple. echo foobar barfoo | wc. Mais remarquez: dans la page de manuel de bash vous verrez qu’elle est notée <(list). Donc, fondamentalement, vous pouvez rediriger la sortie de plusieurs (!) Commandes.

Remarque: techniquement, lorsque vous dites < <, vous ne faites pas référence à une chose mais à deux redirection avec un seul < et au traitement de la redirection de la sortie de <( . . .).

Maintenant que se passe-t-il si nous ne faisons que traiter la substitution?

$ echo <(echo bar)
/dev/fd/63

Comme vous pouvez le constater, le shell crée le descripteur de fichier temporaire /dev/fd/63 où la sortie est renvoyée (ce qui, selon réponse de Gilles , est un canal anonyme). Cela signifie que < redirige ce descripteur de fichier en tant qu'entrée dans une commande.

Un exemple très simple consisterait à effectuer une substitution de processus de la sortie de deux commandes echo dans wc:

$ wc < <(echo bar;echo foo)
      2       2       8

Donc, ici, nous faisons en sorte que Shell crée un descripteur de fichier pour toutes les sorties qui se produisent dans la parenthèse et redirige celui-ci en entrée vers wc. Comme prévu, wc reçoit ce flux de deux commandes echo, qui seules produiraient deux lignes, chacune ayant un mot et, de manière appropriée, nous avons 2 mots, 2 lignes et 6 caractères plus deux nouvelles lignes comptées.

Note latérale: La substitution de processus peut être appelée bashisme (commande ou structure utilisable à un stade avancé). des shells comme bash, mais non spécifié par POSIX), mais il a été implémenté dans ksh avant l'existence de bash comme page de manuel ksh et cette réponse suggère. Des shells tels que tcsh et mksh n'ont cependant pas de substitution de processus. Alors, comment pourrions-nous rediriger la sortie de plusieurs commandes dans une autre commande sans substitution de processus? Regroupement plus tuyauterie!

$ (echo foo;echo bar) | wc
      2       2       8

Effectivement, c'est la même chose que l'exemple ci-dessus. Cependant, cela est différent de la substitution de processus, puisque nous faisons stdout de tout le sous-shell et stdin de wclié au tuya . D'autre part, la substitution de processus fait en sorte qu'une commande lise un descripteur de fichier temporaire.

Donc, si nous pouvons faire un regroupement avec une tuyauterie, pourquoi avons-nous besoin d'une substitution de processus? Parce que parfois nous ne pouvons pas utiliser de tuyauterie. Prenons l'exemple ci-dessous - Comparaison des sorties de deux commandes avec diff (qui nécessite deux fichiers et, dans ce cas, nous lui donnons deux descripteurs de fichier).

diff <(ls /bin) <(ls /usr/bin)
101
Sergiy Kolodyazhnyy

< < est une erreur de syntaxe:

$ cat < <
bash: syntax error near unexpected token `<'

< <() est substitution de processus (<()) combiné avec la redirection (<):

Un exemple artificiel:

$ wc -l < <(grep ntfs /etc/fstab)
4
$ wc -l <(grep ntfs /etc/fstab)
4 /dev/fd/63

Avec la substitution de processus, le chemin d'accès au descripteur de fichier est utilisé comme un nom de fichier. Si vous ne voulez pas (ou ne pouvez pas) utiliser directement un nom de fichier, vous combinez la substitution de processus avec la redirection.

Pour être clair, il n'y a pas d'opérateur < <.

22
muru

< < est une erreur de syntaxe. Vous voulez probablement dire command1 < <( command2 ), qui est une simple redirection d’entrée suivie d’un processus de substitution; elle est très similaire mais n’équivaut pas à:

command2 | command1

La différence en supposant que vous exécutez bash est command1 est exécutée dans un sous-shell dans le deuxième cas alors qu'il est exécuté dans le shell actuel dans le premier. Cela signifie que les variables définies dans command1 ne seront pas perdues avec la variante de substitution de processus.

10
jlliagre

< < donnera une erreur de syntaxe. L'utilisation appropriée est la suivante:

Expliquer à l'aide d'exemples:

Exemple pour < <():

while read line;do
   echo $line
done< <(ls)

Dans l'exemple ci-dessus, l'entrée dans la boucle while proviendra de la commande ls qui peut être lue ligne par ligne et echoed dans la boucle.

<() est utilisé pour la substitution de processus. Vous trouverez plus d'informations et d'exemples sur <() à l'adresse suivante:

substitution de processus et pipe

9
snoop