Difference between revisions of "HelloBash Ep2"

From Tmplab
Line 25: Line 25:
 
Voilà ce que fait -un peu caricaturalement- un terminal comme à l'époque du teletype :  
 
Voilà ce que fait -un peu caricaturalement- un terminal comme à l'époque du teletype :  
  
# Recevoir un text (input/stdin) saisi : création d'une chaîne de caractères individuels
+
# Recevoir un text (input/STDIN) : création d'une chaîne de caractères individuels
 
# Analyser le texte saisi  (parsing) : découpe de la chaîne de texte en commande + arguments
 
# Analyser le texte saisi  (parsing) : découpe de la chaîne de texte en commande + arguments
# Exécuter la commande avec ses arguments (exec[vlpe]) et retransmission des chaînes de sortie standard (output/stdout) et d'erreurs (errors/stderr)
+
# Exécuter la commande avec ses arguments (exec[vlpe]) et retransmission des chaînes de sortie standard (output/STDOUT) et d'erreurs (errors/STDERR)
  
  
'''Ce qu'on appelle flux (ou streams) ce sont les différentes chaînes de caractères en transit : les stdin, stdout, stderr'''
+
'''Ce qu'on appelle flux (ou streams) ce sont les différentes chaînes de caractères en transit : les STDIN, STDOUT, STDERR'''
 
   
 
   
 
Bash permet de manipuler ces flux. Force du shell : combiner des commandes existantes entre elles, via les flux.
 
Bash permet de manipuler ces flux. Force du shell : combiner des commandes existantes entre elles, via les flux.
Line 52: Line 52:
 
= Les redirections de flux =
 
= Les redirections de flux =
  
== stdin, stdout, stderr ==
+
== STDIN, STDOUT, STDERR ==
  
  
Line 63: Line 63:
 
   lrwx------ 1 <...> 2 -> /dev/pts/4
 
   lrwx------ 1 <...> 2 -> /dev/pts/4
 
   lr-x------ 1 <...> 3 -> /proc/6017/fd
 
   lr-x------ 1 <...> 3 -> /proc/6017/fd
   '''$ ls -go /dev/stdin'''
+
   '''$ ls -go /dev/STDIN'''
   lrwxrwxrwx 1 <...> /dev/stdin -> /proc/self/fd/0
+
   lrwxrwxrwx 1 <...> /dev/STDIN -> /proc/self/fd/0
  
 
Ce sont des descripteurs de fichiers (file descriptors/fd) et ils peuvent être manipulés en les substituant avec d'autres descripteurs de fichiers.  
 
Ce sont des descripteurs de fichiers (file descriptors/fd) et ils peuvent être manipulés en les substituant avec d'autres descripteurs de fichiers.  
Line 70: Line 70:
 
Ils ont chacun un numéro standardisé :
 
Ils ont chacun un numéro standardisé :
  
* 0 == stdin
+
* 0 == STDIN
* 1 == stdout
+
* 1 == STDOUT
* 2 == stderr
+
* 2 == STDERR
  
  
Line 84: Line 84:
 
   $ commande redirection_2 redirection_1
 
   $ commande redirection_2 redirection_1
  
== Redirections de stdin ==
+
=== Utilisation des flux standards pour les redirections===
=== Redirection par un descripteur de fichier : < ===
 
  $ cat < /etc/hosts
 
  $ mysql < ~/query.sql
 
  
=== Redirection par un document (HEREWORD) : << ===
+
'''Les numéros de flux (0,1,2) sont utilisés pour rediriger les flux par défaut'''
 +
 
 +
Par défault, les opérateurs qui manipulent les flux input utilisent le numéro de fd 0 (STDIN).
 +
 
 +
Par défault, les opérateurs qui manipulent les flux output utilisent le numéro de fd 1 (STDOUT)
 +
 
 +
Pour signifier qu'on veut rediriger vers un flux existant, on le préfixe avec un "&", sinon Bash pense que vous parlez du fichier nommé "1", pas de /proc/self/fd/1.
 +
 
 +
== Redirections de STDOUT et STDERR ==
 +
 
 +
=== Redirection simple : > et >> ===
 +
 
 +
Un simple > remplace un contenu existant vers une sortie existante ou créable
 +
 
 +
Un double >>  concatène la sortie vers une sortie existante ou créable
 +
 
 +
  $ ls > /tmp/list.txt
 +
  $ ls >> /tmp/list.txt
 +
 
 +
On peut décider de créer un fichier avec les syntaxes suivantes, qui ne prennent aucune entrée
 +
 
 +
  $ : > /tmp/foo
 +
  $ > /tmp/foo
 +
 
 +
 
 +
=== Redirections vers un fichier sans écraser le contenu existant : >| et >>| ===
 +
 
 +
Si on active la fonction <code>noclobber</code> de Bash, on ne peut modifier un fichier existant à moins d'utiliser cette syntaxe particulière
 +
  $ echo "ok" > /tmp/test
 +
  $ set -o noclobber
 +
  $ echo "bad" > /tmp/test
 +
  $ echo "works" |> /tmp/test
 +
 
 +
=== Redirection de STDERR : 2> ===
 +
 
 +
'''Si on veut la sortie et les erreurs dans des fichiers différents'''
 +
 
 +
  $ curl https://www.tmplab.org 1>/tmp/std 2>/tmp/err
 +
 
 +
'''Si on veut enregistrer à la fois les erreurs et la sortie dans un même fichier.'''
 +
 
 +
  $  ls -lh /foo  > /tmp/out 2>&1
 +
  $ cat /tmp/out
 +
 
 +
Attention, il faut bien rediriger d'abord la sortie sortie vers le fichier, sinon STDERR reste sur STDOUT
 +
 
 +
  $  ls -lh /foo  > /tmp/out 2>&1
 +
  ls: impossible d'accéder à '/foo': Aucun fichier ou dossier de ce type
 +
 
 +
=== Redirection de STDERR et STDOUT : &> ===
 +
 
 +
On utilise cette syntaxe pour rediriger 1 et 2 vers un descripteur de fichier commun
 +
 
 +
  $ ls -lh /proc/self/fd &>/tmp/out
 +
  $ cat /tmp/out
 +
 
 +
On voit que 1 et 2 sont bien pointées vers /dev/null
 +
 
 +
=== Envoi de caractères dans STDERR : >&2 ===
 +
 
 +
On peut rediriger une sortie vers un flux numéroté comme STDERR.
 +
 
 +
  $ echo "Ooops" 1>/dev/null >&2
 +
 
 +
== Redirections de STDIN ==
 +
 
 +
=== Redirection par un contenu de fichier : < ===
 +
 
 +
Cette syntaxe redirige par défaut le flux numéroté "0" vers le fichier indiqué
 +
 
 +
  $ ls /proc/self/fd -lh </tmp/out
 +
 
 +
Elle peut être utile pour lancer une série de commandes SQL :
 +
 
 +
  $ echo "show databases" > /tmp/query.sql
 +
  $ mysql < /tmp/query.sql
 +
 
 +
 
 +
=== Redirection par un document (Here Documents) : << ===
 +
 
 +
Un ''Here Documents'' crée une entrée multiligne qui est passée en STDIN à la commande.
 +
 
 +
L'entrée est démarquée par la suite de caractères qui suit les caractères "<<". Elle se termine quand la suite de caractères est répétée sur une seule ligne avant un retour à la ligne, sans espaces.
  
 
   $ cat << HEREDOC
 
   $ cat << HEREDOC
 
   hello Bash
 
   hello Bash
 +
  It's great.
 +
  We love it!
 
   HEREDOC
 
   HEREDOC
  
===  Redirection par une chaîne (HEREDOC) : <<< ===  
+
On peut créer des fichiers complexes en associant à un HEREDOC
 +
 
 +
  $ cat 1>/tmp/out << SOMETHING
 +
  my file
 +
  is
 +
  multiline!
 +
  SOMETHING
 +
 
 +
===  Redirection par une chaîne (Here Strings) : <<< ===  
 +
 
 +
Un
 +
 
 
   $ cat <<< "Hello Bash"
 
   $ cat <<< "Hello Bash"
 +
  $ bash <<< "sleep 1"
 +
 +
== Création et fermeture de flux ==
 +
 +
=== Fermeture d'un flux : N>&-  ===
 +
 +
Cette commande supprime le flux numéroté "N" qu'on place avant le ">&-" : il disparaît de la liste des descripteurs de fichiers du processus.
 +
 +
  $ bash -c "echo $$; exec 2>&-; sleep 60;"
 +
  $ ls -lh /proc/PID/fd # PID = le numéro du processus retourné par la commande ci-dessus
 +
 
 +
 +
=== Création de nouveaux flux : N< ===
 +
 +
Cette syntaxe génère un flux numéroté "N".
 +
 +
Ici, on créé un flux "5" en copiant la cible de 2, qui lui-même change deux fois de directions.
 +
 +
  $ ls -lh /proc/self/fd 2>/tmp/out 5<&2 2>/dev/null
 +
 +
 +
=== Ouverture d'un flux pour entrée et sortie : N<> ===
 +
 +
Cette syntaxe permet de définir un flux numéroté N qui peut être utilisé à la fois pour lire et pour écrire.
 +
 +
  $ echo 1234567890 > /tmp/file
 +
  $ exec 3<> /tmp/file
 +
  $ read -n 4 <&3
 +
  $ echo -n . >&3
 +
  $ exec 3>&-
 +
  $ cat /tmp/file
 +
 +
Par exemple on peut appeler un flux TCP :
 +
 +
  $ exec 3<>/dev/tcp/neverssl.com/80
 +
  $ echo -e "GET / HTTP/1.1\r\nhost: neverssl.com\r\nConnection: close\r\n\r\n" >&3
 +
  $ cat <&3
 +
 +
=== Ouverture d'un flux "anonyme" : <( command ) ===
 +
 +
Cette syntaxe crée un type de descripteur de fichier particulier (un FIFO) qui contient le résultat de la commande appelée.
 +
 +
Par exemple, elle évite pour la commande suivante de stocker les résultats des commandes dans deux fichiers avant de les comparer.
 +
 +
  $ diff -y <(man chattr) <(man chmod)
  
> et >>
 
>| et >>|
 
<
 
<<
 
<<<
 
<(...)
 
[n]<
 
[n]<>
 
 
= Les Pipes =
 
= Les Pipes =
 +
 +
=== Le pipe simple : | ===
 +
 +
Cette syntaxe permet de transmettre la STDOUT d'une commande comme STDIN d'une autre commande.
 +
 +
  $ echo "hello" | ls  -lh /proc/self/fd
 +
 +
On voit que le fd "0" est devenu un "pipe" : les données sont "versées" par la première commande dans la seconde.
 +
 +
Exemple. La première commande liste des fichiers. Cette liste est transmise à grep qui ne sélectionne que les lignes contenant un mot particulier
 +
 +
  $ ls -lh /etc | grep shadow
 +
 +
Cette syntaxe toute simple ouvre beaucoup de possibilités. Quelques exemples.
 +
 +
  $ ls -lh /etc | wc
 +
  $ ls -lh /etc | head
 +
  $ ls -lh /etc | tail
 +
  $ ls -lh /etc | tr "aeiou" "_"
 +
  $ ls -lh /etc |fmt -g 20  -s -
 +
 +
Les commandes comme <code>fmt</code> acceptent souvent le caractère "-" pour signaler que les données à traiter sont fournies en input
 +
 +
On peut se retrouver à enchaîner les pipes.
 +
 +
  $ cat *.txt | sort | uniq > result-file
 +
 +
=== Le pipe de redirection multi sorties :  |& ===
 +
 +
  $ ls /foo |& cat >/tmp/out
 +
 +
cmd | tee file Redirect stdout of cmd to a file and print it to screen.
 +
exec {filew}> file Open a file for writing using a named file descriptor called {filew} (bash 4.1+).
 +
cmd 3>&1 1>&2 2>&3 Swap stdout and stderr of cmd.
 +
cmd > >(cmd1) 2> >(cmd2) Send stdout of cmd to cmd1 and stderr of cmd to cmd2.
 +
cmd1 | cmd2 | cmd3 | cmd4
 +
echo ${PIPESTATUS[@]} Find out the exit codes of all piped commands.
  
 
= Sources =
 
= Sources =
 +
https://catonmat.net/ftp/bash-redirections-cheat-sheet.pdf
 
https://xavcc.frama.io/introduction-stream/
 
https://xavcc.frama.io/introduction-stream/
 
https://developer.ibm.com/tutorials/l-lpic1-103-2/
 
https://developer.ibm.com/tutorials/l-lpic1-103-2/
 
https://developer.ibm.com/tutorials/l-lpic1-103-4/
 
https://developer.ibm.com/tutorials/l-lpic1-103-4/
 
https://www.gnu.org/software/bash/manual/html_node/Redirections.html
 
https://www.gnu.org/software/bash/manual/html_node/Redirections.html
 +
https://www.tldp.org/LDP/abs/html/io-redirection.html
 +
https://brennan.io/2015/01/16/write-a-shell-in-c/

Revision as of 22:07, 13 October 2019

contenu en cours de rédaction

Un peu de théorie

Le shell que vous utilisez est probablement un stty

Un teletype
 $ tty
 /dev/pts/0
 $ whatis pts
 pts (4)              - pseudoterminal master and slave
 $ stty
 speed 38400 baud; line = 0;
 -brkint -imaxbel


Un pts/stty est une simulation de tty, qui signifie teletype, un descendant du télégraphe, qui communiquait par modem et ligne télégraphique avec d'autres teletypes. Ici une analyse poussée, de ce qu'est un tty et les teletype.

Un tty est un programme dont la fonctionnalité première est de gèrer du flux de texte.

Voilà ce que fait -un peu caricaturalement- un terminal comme à l'époque du teletype :

  1. Recevoir un text (input/STDIN) : création d'une chaîne de caractères individuels
  2. Analyser le texte saisi (parsing) : découpe de la chaîne de texte en commande + arguments
  3. Exécuter la commande avec ses arguments (exec[vlpe]) et retransmission des chaînes de sortie standard (output/STDOUT) et d'erreurs (errors/STDERR)


Ce qu'on appelle flux (ou streams) ce sont les différentes chaînes de caractères en transit : les STDIN, STDOUT, STDERR

Bash permet de manipuler ces flux. Force du shell : combiner des commandes existantes entre elles, via les flux.

Métaphore: un teletype qui enverrait un message en Italie, récupèrerait la sortie, remplacerait certains mots et retransmettrait le résultat en Allemagne.

Dans le cas d'un stty, pas besoin d'édition papier, tout se fait avec des chaînes de texte.

Les types de manipulation

Nous allons voir les manipulations de flux suivantes

  1. les redirections
  2. les pipes (pipelines/tubes)


Dans les deux cas nous verrons des commandes utiles associées à ces manipulations


Les redirections de flux

STDIN, STDOUT, STDERR

Un terminal est instancié avec 3 flux natifs numérotés 0, 1, et 2

 $ ls -go /proc/self/fd
 total 0
 lrwx------ 1 <...> 0 -> /dev/pts/4
 lrwx------ 1 <...> 1 -> /dev/pts/4
 lrwx------ 1 <...> 2 -> /dev/pts/4
 lr-x------ 1 <...> 3 -> /proc/6017/fd
 $ ls -go /dev/STDIN
 lrwxrwxrwx 1 <...> /dev/STDIN -> /proc/self/fd/0

Ce sont des descripteurs de fichiers (file descriptors/fd) et ils peuvent être manipulés en les substituant avec d'autres descripteurs de fichiers.

Ils ont chacun un numéro standardisé :

  • 0 == STDIN
  • 1 == STDOUT
  • 2 == STDERR


La redirection permet aux descripteurs de fichier des commandes d'être dupliqués, ouverts, fermés, de se référer à différents fichiers et de modifier les fichiers lus et écrits par la commande.

Les opérateurs de redirection peuvent précéder ou apparaître n'importe où dans une commande simple ou peuvent suivre une commande. Les redirections sont traitées dans l'ordre dans lequel elles apparaissent, de gauche à droite.

Les deux opération suivantes auront des résultats différents

 $ commande redirection_1 redirection_2
 $ commande redirection_2 redirection_1

Utilisation des flux standards pour les redirections

Les numéros de flux (0,1,2) sont utilisés pour rediriger les flux par défaut

Par défault, les opérateurs qui manipulent les flux input utilisent le numéro de fd 0 (STDIN).

Par défault, les opérateurs qui manipulent les flux output utilisent le numéro de fd 1 (STDOUT)

Pour signifier qu'on veut rediriger vers un flux existant, on le préfixe avec un "&", sinon Bash pense que vous parlez du fichier nommé "1", pas de /proc/self/fd/1.

Redirections de STDOUT et STDERR

Redirection simple : > et >>

Un simple > remplace un contenu existant vers une sortie existante ou créable

Un double >> concatène la sortie vers une sortie existante ou créable

 $ ls > /tmp/list.txt
 $ ls >> /tmp/list.txt

On peut décider de créer un fichier avec les syntaxes suivantes, qui ne prennent aucune entrée

 $ : > /tmp/foo
 $ > /tmp/foo


Redirections vers un fichier sans écraser le contenu existant : >| et >>|

Si on active la fonction noclobber de Bash, on ne peut modifier un fichier existant à moins d'utiliser cette syntaxe particulière

 $ echo "ok" > /tmp/test
 $ set -o noclobber
 $ echo "bad" > /tmp/test
 $ echo "works" |> /tmp/test

Redirection de STDERR : 2>

Si on veut la sortie et les erreurs dans des fichiers différents

 $ curl https://www.tmplab.org 1>/tmp/std 2>/tmp/err 

Si on veut enregistrer à la fois les erreurs et la sortie dans un même fichier.

 $  ls -lh /foo  > /tmp/out 2>&1
 $ cat /tmp/out

Attention, il faut bien rediriger d'abord la sortie sortie vers le fichier, sinon STDERR reste sur STDOUT

 $  ls -lh /foo  > /tmp/out 2>&1
 ls: impossible d'accéder à '/foo': Aucun fichier ou dossier de ce type

Redirection de STDERR et STDOUT : &>

On utilise cette syntaxe pour rediriger 1 et 2 vers un descripteur de fichier commun

 $ ls -lh /proc/self/fd &>/tmp/out
 $ cat /tmp/out

On voit que 1 et 2 sont bien pointées vers /dev/null

Envoi de caractères dans STDERR : >&2

On peut rediriger une sortie vers un flux numéroté comme STDERR.

 $ echo "Ooops" 1>/dev/null >&2

Redirections de STDIN

Redirection par un contenu de fichier : <

Cette syntaxe redirige par défaut le flux numéroté "0" vers le fichier indiqué

 $ ls /proc/self/fd -lh </tmp/out

Elle peut être utile pour lancer une série de commandes SQL :

 $ echo "show databases" > /tmp/query.sql
 $ mysql < /tmp/query.sql


Redirection par un document (Here Documents) : <<

Un Here Documents crée une entrée multiligne qui est passée en STDIN à la commande.

L'entrée est démarquée par la suite de caractères qui suit les caractères "<<". Elle se termine quand la suite de caractères est répétée sur une seule ligne avant un retour à la ligne, sans espaces.

 $ cat << HEREDOC
 hello Bash
 It's great.
 We love it!
 HEREDOC

On peut créer des fichiers complexes en associant à un HEREDOC

 $ cat 1>/tmp/out << SOMETHING
 my file
 is 
 multiline!
 SOMETHING 

Redirection par une chaîne (Here Strings) : <<<

Un

 $ cat <<< "Hello Bash"
 $ bash <<< "sleep 1"

Création et fermeture de flux

Fermeture d'un flux : N>&-

Cette commande supprime le flux numéroté "N" qu'on place avant le ">&-" : il disparaît de la liste des descripteurs de fichiers du processus.

 $ bash -c "echo $$; exec 2>&-; sleep 60;"
 $ ls -lh /proc/PID/fd # PID = le numéro du processus retourné par la commande ci-dessus
 

Création de nouveaux flux : N<

Cette syntaxe génère un flux numéroté "N".

Ici, on créé un flux "5" en copiant la cible de 2, qui lui-même change deux fois de directions.

 $ ls -lh /proc/self/fd 2>/tmp/out 5<&2 2>/dev/null


Ouverture d'un flux pour entrée et sortie : N<>

Cette syntaxe permet de définir un flux numéroté N qui peut être utilisé à la fois pour lire et pour écrire.

 $ echo 1234567890 > /tmp/file
 $ exec 3<> /tmp/file
 $ read -n 4 <&3
 $ echo -n . >&3
 $ exec 3>&-
 $ cat /tmp/file

Par exemple on peut appeler un flux TCP :

 $ exec 3<>/dev/tcp/neverssl.com/80
 $ echo -e "GET / HTTP/1.1\r\nhost: neverssl.com\r\nConnection: close\r\n\r\n" >&3
 $ cat <&3

Ouverture d'un flux "anonyme" : <( command )

Cette syntaxe crée un type de descripteur de fichier particulier (un FIFO) qui contient le résultat de la commande appelée.

Par exemple, elle évite pour la commande suivante de stocker les résultats des commandes dans deux fichiers avant de les comparer.

 $ diff -y <(man chattr) <(man chmod) 

Les Pipes

Le pipe simple : |

Cette syntaxe permet de transmettre la STDOUT d'une commande comme STDIN d'une autre commande.

 $ echo "hello" | ls  -lh /proc/self/fd

On voit que le fd "0" est devenu un "pipe" : les données sont "versées" par la première commande dans la seconde.

Exemple. La première commande liste des fichiers. Cette liste est transmise à grep qui ne sélectionne que les lignes contenant un mot particulier

 $ ls -lh /etc | grep shadow

Cette syntaxe toute simple ouvre beaucoup de possibilités. Quelques exemples.

 $ ls -lh /etc | wc
 $ ls -lh /etc | head
 $ ls -lh /etc | tail
 $ ls -lh /etc | tr "aeiou" "_"
 $ ls -lh /etc |fmt -g 20  -s -

Les commandes comme fmt acceptent souvent le caractère "-" pour signaler que les données à traiter sont fournies en input

On peut se retrouver à enchaîner les pipes.

 $ cat *.txt | sort | uniq > result-file

Le pipe de redirection multi sorties : |&

 $ ls /foo |& cat >/tmp/out

cmd | tee file Redirect stdout of cmd to a file and print it to screen. exec {filew}> file Open a file for writing using a named file descriptor called {filew} (bash 4.1+). cmd 3>&1 1>&2 2>&3 Swap stdout and stderr of cmd. cmd > >(cmd1) 2> >(cmd2) Send stdout of cmd to cmd1 and stderr of cmd to cmd2. cmd1 | cmd2 | cmd3 | cmd4 echo ${PIPESTATUS[@]} Find out the exit codes of all piped commands.

Sources

https://catonmat.net/ftp/bash-redirections-cheat-sheet.pdf https://xavcc.frama.io/introduction-stream/ https://developer.ibm.com/tutorials/l-lpic1-103-2/ https://developer.ibm.com/tutorials/l-lpic1-103-4/ https://www.gnu.org/software/bash/manual/html_node/Redirections.html https://www.tldp.org/LDP/abs/html/io-redirection.html https://brennan.io/2015/01/16/write-a-shell-in-c/