Les mécanismes du shell

Commandes internes et externes

Les commandes externes

Une commande externe est un fichier présent dans l'arborescence.

Quand un utilisateur exécute la commande ls, le shell demande au noyau Linux d'exécuter le fichier /bin/ls

Sont considérés comme commandes externes, tous les fichiers au format binaire exécutable ainsi que tous les fichiers au format texte représentant un script de commandes.

La commande file indique le type de données contenues dans un fichier.

$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped

$ file monscript
monscript: POSIX shell script text executable

L'argument de la commande file est un nom de fichier indiqué en relatif ou en absolu

Etiquettes: 

Les commandes internes

Une commande interne est intégrée au processus shell.

Elle n'a aucune correspondance avec un fichier sur le disque.

La commande type indique si une commande est interne ou externe.

$ type cd
cd est une primitive du shell

$ type ls
ls est un alias vers « ls --color=auto »

La commande type prend en argument le nom d'une commande. Si cette dernière n'est pas une commande interne, elle est recherchée dans les répertoires indiqués dans la variable PATH

Etiquettes: 

Les commandes internes et externes

Certaines commandes ont une implémentation interne et externe.

  • La commande interne est exécutée en priorité.
  • L'exécution d'une commande interne est plus rapide.
  • La commande type, indique que la commande est interne mais ne précise pas qu'elle peut également être une commande externe.

La commande pwd est une commande interne :

$ type pwd
pwd est une primitive du shell

La commande pwd possède également une implémentation externe :

$ ls -l /bin/pwd
-rwxr-xr-x 1 root root 34376 2010-09-21 20:33 /bin/pwd

Pour forcer l'utilisation de la commande externe, il faut indiquer l'emplacement de la commande :

$ /bin/pwd
/home
$ cd /bin
$ ./pwd
/bin

Affichage à l'écran

La commande interne echo permet d'afficher des données à l'écran.

$ echo Ceci est un exemple
Ceci est un exemple

Il faut échapper l'apostrophe avec l'antislash

$ echo Un exemple avec l\'apostrophe
Un exemple avec l'apostrophe

Il faut échapper les guillemets avec l'antislash

$ echo Un exemple avec des \"guillemets\"
Un exemple avec des "guillemets"

En bash, il faut obligatoirement utilisé l'option "-e" pour utiliser les caractères spéciaux suivants.

  • Le caractère "\n" sert à provoquer un saut de ligne :

$ echo -e "Un exemple\navec un saut de ligne"
Un exemple
avec un saut de ligne

  • Le caractère "\c" supprime le saut de ligne naturel de la commande echo :

Sans le caractère "\c"

$ echo -e "Une ligne" ; echo -e "Une autre ligne"
Une ligne
Une autre ligne

Avec le caractère "\c"

$ echo -e "Une ligne\c" ; echo -e "Une autre ligne"
Une ligneUne autre ligne

L'option -n remplace le caractère "\c"

$ echo -n "Une ligne" ; echo "Une autre ligne"
Une ligneUne autre ligne

  • Le caractère "\t" sert à afficher une tabulation :

$ echo -e "Voici 1 tabulation\tet la suite"
Voici 1 tabulation      et la suite

  • Le caractère "\\" permet d'afficher un antislash :

$ echo -e "Afficher un antislash\\"
Afficher un antislash\

Liste des caractères spéciaux :

\0NNN Valeur d'un caractère exprimé en octal
\\ Antislash
\a Bip
\b Effacement du caractère précédent
\c Suppression du saut de ligne en fin de ligne
\f Saut de page
\n Saut de ligne
\r Retour chariot
\t Tabulation horizontale
\v Tabulation verticale

 

Le caractère ~ (tilde)

Le caractère "~" représente le répertoire HOME de l'utilisateur.

Par exemple, pour l'utilisateur "toto"

$ cd ~
$ pwd
/home/toto

Copier le fichier "fichier1" du répertoire /tmp dans le répertoire /home/toto

$ cd /tmp
$ cp fichier1 ~

Copier le fichier "fichier2" du répertoire /tmp dans le répertoire /home/toto/mesfichiers

$ cd /tmp
$ cp fichier2 ~/mesfichiers

Si le caractère "~" est immédiatement suivi d'un mot, ce dernier est considéré comme un nom d'utilisateur.

Copier le fichier "fichier3" du répertoire /home/toto vers le répertoire /home/titi

$ cd ~
$ pwd
/home/toto
$ cp fichier3 ~titi

Copier le fichier "fichier4" du répertoire /home/toto vers le répertoire /home/titi/tmp

$ cd ~
$ pwd
/home/toto
$ cp fichier4 ~titi/tmp

 

La commande interne cd

La commande cd, sans arguments, permet à l'utilisateur de se placer dans son répertoire HOME.

$ pwd
/tmp
$ cd
$ pwd
/home/toto

Identique en utilisant le caractère "~"

$ cd ~

Pour se rendre dans le répertoire HOME de tata :

$ pwd
/home/toto
$ cd ~tata
$ pwd
/home/tata

Pour revenir au répertoire précédent avec la commande "cd -" :

$ cd -
$ pwd
/home/toto

Remplacement de noms de fichiers

Expressions simples

Attention, certaines de ces expressions ne fonctionnent pas toutes avec les mêmes shell.
Pour certaines, elles sont compatibles BASH, pour d'autres ZSH et certaines ne fonctionnent qu'avec KSH.

Le caractère "*" permet de remplacer n'importe quel caractère.

$ ls
fichier1 fichier2.a monfichier tOnfichier.b

  • Afficher uniquement les fichiers se terminant par ".a" :

$ ls *.a
fichier2.a

  • Afficher uniquement les fichiers commançant par "f" :

$ ls f*
fichier1 fichier2.a

Le caractère "?" représente un caractère quelconque.

  • Afficher uniquement les fichiers ayant une extension composée d'un seul caractère :

$ ls *.?
fichier2.a tOnfichier.b

  • Afficher uniquement les fichiers composés de 8 caractères :

$ ls ????????
fichier1

Les caractères "[ ]" permettent d'indiquer la liste des caractères que l'on recherche à une position bien précise dans le noms des fichiers.

  • Afficher les fichiers commençant par la lettre "f" ou "t" et se terminant
    par le "." suivi d'une lettre minuscule :

$ ls [ft]*.[a-z]
fichier2.a tOnfichier.b

  • Afficher tous les fichiers ayant en deuxième caractère une lettre majuscule, ou un chiffre,
    ou la lettre "e" :

% ls ?[A-Z0-9e]*
cOucou  f1chier  F2chier  Hello

  • Afficher tous les fichiers ne commançant pas par une lettre minuscule :

$ ls [!a-z]*
1coucou  Coucou  F2chier  Fichier  Hello

  • Supprimer tous les fichiers se terminant par .b ou .c :

% rm -i *.b *.c
rm : supprimer fichier vide «fichier1.b» ? y
rm : supprimer fichier vide «fichier1.c» ? y

 

Expressions complexes

Les expressions complexes sont compatible BASH à condition d'activer l'option extglob avec la commande shopt (shopt -s extglob).

$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c

  • ?(expression)

L'expression sera présente 0 ou 1 fois.

Afficher tous les fichiers dont le nom contient 0 ou 1 fois "159"

$ ls fichier?(159).log
fichier159.log  fichier.log

  • *(expression)

L'expression sera présente entre 0 et x fois.

Afficher tous les fichiers dont le nom contient 0 ou x fois "159"

$ ls fichier*(159).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier.log

  • +(expression)

L'expression sera présente entre 1 et x fois.

Afficher tous les fichiers dont le nom contient 1 ou x fois "159"

$ ls fichier+(159).log
fichier159159159.log  fichier159159.log  fichier159.log

  • @(expression)

L'expression sera présente exactement 1 fois.

Afficher tous les fichiers dont le nom contient 1 fois "159"

$ ls fichier@(159).log
fichier159.log

  • !(expression)

L'expression ne sera pas présente.

Afficher tous les fichiers dont le nom ne contient pas 1 fois "159"

$ ls fichier!(159).log
fichier159159159.log  fichier159160.log  fichier161.log
fichier159159.log     fichier160.log     fichier.log

Afficher tous les fichiers dont le nom ne contient pas "fichier"

$ ls !(fichier*)
1coucou  Coucou    coucou.s  coucou.uvw  F2chier  Hello.txt
cOucou   coucou.r  coucou.t  f1chier     Fichier

Une barre verticale dans une expression correspond à "ou bien"

Afficher tous les fichiers dont le nom contient 1 fois "159" ou "160"

$ ls fichier@(159|160).log
fichier159.log  fichier160.log

Afficher tous les fichiers dont le nom contient 1 ou x fois "159" ou "161"

$ ls fichier+(159|161).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Afficher tous les fichiers dont le nom contient 1 fois ( 1 ou x fois "159" ou "161")

$ ls fichier@(+(159)|+(161)).log
fichier159159159.log  fichier159159.log  fichier159.log  fichier161.log

Séparateur de commandes

Le caractère ";" permet d'écrire plusieurs commandes sur une même ligne. Toutes les commandes sont exécutées dans l'ordre.

$ pwd
/home/toto
$ ls
1coucou   coucou.t    fichier159159159.log  fichier161.log  fichier.log
cOucou    coucou.uvw  fichier159159.log     fichier1.a      Hello.txt
Coucou    f1chier     fichier159160.log     fichier1.abc
coucou.r  F2chier     fichier159.log        fichier1.b
coucou.s  Fichier     fichier160.log        fichier1.c
$ mkdir tmp; chmod 755 tmp; chown toto:toto tmp; cd tmp; pwd
/home/toto/tmp

Redirections

Les redirections sont souvent utilisées dans les commandes Linux.
Elles permettent de récupérer le résultat d'une ou plusieurs commandes dans un ou plusieurs fichiers ou de faire lire le contenu d'un fichier à une commande.

Entrée et sorties standard des commandes

Les commandes Linux ont par défaut 3 descripteurs de fichier différent.

  • Entrée standard :

L'entrée standard d'une commande correspond au descripteur de fichier 0.
Les commandes qui attendent des informations de la part de l'utilisateur déclenche une requête de lecture sur le descripteur 0.
Par défaut, ce descripteur est associé au terminal et donc par une saisie au clavier.

  • Sortie standard :

La sortie standard d'une commande correspond au descripteur de fichier 1.
Le résultat d'une commande est, par défaut, affichée à l'écran via ce descripteur mais il peut être redirigé dans un fichier.

  • Sortie d'erreur standard :

La sortie d'erreur standard d'une commande correspond au descripteur de fichier 2.
Quand une commande rencontre une erreur, celle-ci est retournée à l'écran via le descripteur 2 mais elle peut également être retournée dans un fichier.

Redirection des sorties vers un fichier

Cette redirection permet d'écrire dans un fichier le résultat d'une commande au lieu de l'afficher à l'écran.

  • Sortie standard (descripteur de fichier 1)

Simple redirection

$ commande > fichier
ou
$ commande 1> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, il est tout simplement écrasé.

Récupérer le résultat de la commande ls dans un fichier "liste"

$ ls > liste
$ cat liste
1coucou
cOucou
Coucou

Double redirection

Elle permet de concaténer le résultat d'une commande au contenu d'un fichier déjà existant.

$ commande >> fichier
ou
$ commande 1>> fichier

Si le fichier n'existe pas, il est automatiquement créé. S'il existe déjà, le résultat de la commande est ajouté au fichier.

$ pwd >> liste
$ cat liste
1coucou
cOucou
Coucou
/home/toto

  • Sortie d'erreur standard (descripteur de fichier 2)

Simple redirection

$ commande 2> fichier

$ ls /root 2> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

Double redirection

$ commande 2>> fichier

$ mkdir /root/tmp 2>> erreur
$ cat erreur
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
mkdir: impossible de créer le répertoire «/root/tmp»: Permission non accordée

  • Sortie standard et sortie d'erreur standard

Il est possible de rediriger plusieurs sorties sur une même ligne de commande.

$ commande 1> fichier1 2> fichier2
ou
$ commande 2> fichier2 1> fichier1

$ find /etc -name smb.conf 1> resultat 2> erreur
$ cat resultat
/etc/samba/smb.conf
$ cat erreur
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée

Si l'on ne souhaite pas afficher et/ou enregistrer les erreurs retournées, il est possible de rediriger le descripteur 2 vers un fichier spécial existant sur tous les systèmes Linux "/dev/null"

$ find /etc -name smb.conf 1> resultat 2> /dev/null
$ cat resultat
/etc/samba/smb.conf
$ cat /dev/null
$
 

Redirection de l'entrée standard

La redirection de l'entrée standard concerne toutes les commandes attendant une saisie de la part de l'utilisateur sur le descripteur 0 (saisie écran).

$ mail toto
>Coucou
>Comment vas tu ?
>^d (équivalent de CTRL+d)
$

La commande mail lit sur l'entrée standard toutes les données saisies à l'écran. La saisie se termine par la fonction CTRL+d.
Ensuite, la commande mail envoie ces données dans un message à l'utilisateur indiqué (toto).

Il est tout à fait possible d'écrire le contenu du message dans un fichier et de "l'injecter" à la commande mail sur son descripteur 0.

$ commande 0< fichier
ou
$ commande < fichier

$ echo "Coucou, comment vas tu ?" > message
$ cat message
Coucou, comment vas tu ?
$ mail toto < message

 

Redirections avancées

Rediriger les descripteurs 1 et 2 vers le même fichier :

$ commande 1> fichier1 2>&1
ou
$ commande 2> fichier2 1>&2

Le principe consiste à écrire dans un fichier le résulat du descripteur 1 dans le fichier "fichier1" (1> fichier1) puis le résultat du descripteur 2 vers le descripteur 1 (2>&1) et par conséquent dans le fichier "fichier1".

$ find /etc -name smb.conf 1> resultat 2>&1
$ cat resultat
find: "/etc/lvm/cache": Permission non accordée
find: "/etc/lvm/backup": Permission non accordée
find: "/etc/lvm/archive": Permission non accordée
/etc/samba/smb.conf

Attention :

$ commande 2>&1 1> fichier1

Cette syntaxe n'agit pas de la même manière que les précédentes.

En effet, nous indiquons dans un premier temps de rediriger le descripteur 2 (sortie d'erreur standard) vers le descripteur 1, c'est à dire vers la sortie standard qui est, par défaut, l'affichage à l'écran, puis dans un second temps nous redirigeons le descripteur 1 (sortie standard) vers le fichier "fichier1".
En conclusion, tous les messages d'erreurs seront affichés à l'écran et le résulat de la commande dans le fichier "fichier1".

Cela revient à saisir :

$ commande 1> fichier1

La double redirection en lecture :

Elle est principalement utilisée dans les scripts shell.
Elle permet de connecter l'entrée standard d'une commande sur une portion du script

Par exemple, avec la commande mail, cela permet de terminer la saisie du texte avec un caractère ou une chaine de caractère.

$ mail toto<<end
> bonjour
> ceci est un test
> end
$

Pour mettre fin à la commande mail, il suffit simplement de saisir l'expression inscrite juste après les doubles chevrons "<<". Dans l'exemple, la commande se termine après avoir saisi "end".
Identique à la fonction ^d (CTRL+d) en console.

Utilisation dans un script :

$ cat envoiMail.sh
#!/bin/sh
mail toto<<end
Bonjour,
Ceci est un test
Voici la liste des fichiers presents dans ton repertoire :
`ls -lhtr`
end
exit 0

Exécution du script

$ sh ./envoiMail.sh

Fermeture d'un descripteur :

Il est tout à fait possible de fermé un descripteur d'entrée ou de sortie en utilisant les symboles "<&-" ou ">&-" ou "2>&-".

Descripteur d'entrée standard "0" :

$ commande <&-

Descripteur de sortie standard "1" :

$ commande >&-

Descripteur de sortie d'erreur standard "2" :

$ commande 2>&-

Tubes de communications

Un tube de communication, ou pipe en anglais, permet de faire communiquer 2 commandes.
Ce "tube" est représenté par la barre verticale "|".
Le résultat de la commande de gauche est envoyé dans le tube et récupéré par la commande de droite.
C'est à dirte que la sortie standard "1" de la commande de gauche est connecté directement à l'entrée standard "0" de la commande de droite.
La sortie d'erreur standard "2" de la commande de gauche n'est pas envoyée dans le tube.

Pour que cette utilisation est un sens il faut impérativement que la commande de gauche utilise la sortie standard "1" et que la commande de droite utilise l'entrée standard "0".

Les commandes lisant leur entrée standard sont facilement identifiable car elles demandent une saisie au clavier.

Envoyer par mail la liste des users connectés :

$ who | mail toto

Envoyer par mail la liste des fichiers d'un répertoire :

$ ls -lht | mail toto

Envoyer un message à un utilisateur connecté au système :

$ echo "coucou, comment vas tu ?" | write tata

Afficher le nombre de fichiers d'un répertoire :

$ ls | wc -l
29

Envoyer par mail le nombre de fichiers d'un répertoire :

$ ls | wc -l | mail toto

Afficher avec pagination le contenu d'un fichier :

$ cat /etc/passwd | more

Lister le contenu d'un répertoire avec pagination :

$ ls -lht /etc | more

Afficher uniquement certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7
root:/bin/bash

Envoyer par mail certains éléments d'un fichier :

$ cat /etc/passwd | grep root | cut -d':' -f1,7 | mail toto

Afficher à l'écran et enregistrer dans un fichier le contenu d'un répertoire :

$ ls -t | tee listeFichiers
listeFichiers
envoiMail.sh
message
script.sh
$ cat listeFichiers
listeFichiers
envoiMail.sh
message
script.sh

Il est donc tout à fait possible de cumuler autant de commandes que l'on souhaite à partir du moment où l'on respecte l'ordre des commandes et des sorties / entrées standard.

Rediriger la sortie d'erreur standard dans un tube

$ ls -l /root 2>&1 | tee listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée
$ cat listeFichiers
ls: impoossible d'ouvrir le répertoire /root: Permission non accordée

 

Regrouper des commandes

Le regroupement de commandes est utilisé pour rediriger les sorties standards de plusieurs commandes vers un même fichier ou vers un tube ou pour exécuter des commandes dans un même environnement.

$ date ; ls > listeFichiers
lundi 19 septembre 2011, 08:47:11 (UTC+0200)
$ cat listeFichiers
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Dans l'exemple ci-dessus, la commande date et ls sont regroupées grâce au caractère ";".
Le résultat de la commande date est affiché à l'écran alors que le résultat de la commande ls est redirigé dans le fichier "listeFichiers".

Afin de rediriger la sortie standard des 2 commandes au même endroit, ils faut obligatoirement les regroupées avec des parenthèses "()" ou des accolades "{}".

$ ( date ; ls ) > listeFichiers
$ cat listeFichiers
lundi 19 septembre 2011, 08:51:58 (UTC+0200)
1coucou
cOucou
Coucou
coucou.r
coucou.s
coucou.t
coucou.uvw
envoiMail.sh

Les commandes regroupées entre parenthèses sont exécutées par le shell enfant alors que les commandes regroupées entre accolades sont exécutées par le shell parent.

Regroupement avec parenthèses :

$ pwd
/home/toto
$ ( mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ) > listeFichiers
$ cat listeFichiers
/home/toto/temp
fichierTemp
$ pwd
/home/toto

Après exécution des commandes, le répertoire courant est le même qu'avant exécution.

Regroupement avec accolades :

$ pwd
/home/toto
$ { mkdir temp ; cd temp ; touch fichierTemp ; pwd ; ls ; } > listeFichiers
$ pwd
/home/toto/temp
$ cat ../listeFichiers
/home/toto/temp
fichierTemp

Après exécution des commandes, le répertoire courant n'est plus le même qu'avant exécution.

  • L'exécution de commandes regroupées entre accolades est plus rapide que les commandes regroupées entre parenthèses.
  • Le regroupement entre accolades doit obligatoirement se terminer par un point-virgule.
    Les accolades doivent toujours être suivies et précédées par une espace.

     

Généralement, le regroupement entre parenthèses est plus utilisé que le regroupement entre accolades car la syntaxe est plus simple et cela ne modifie pas le shell courant mais pour un gain de performances le regroupement entre accolades est préférable.

Exécuter des commandes en arrière-plan

Pour exécuter des commandes en arrière-plan, il suffit de rajouter à la fin le caractère "&".
Cela permet de récupérer le shell aussitôt après sans être obligé d'attendre la fin de l'exécution de la commande précédente.
Par contre, il est préférable d'utiliser la redirection de la sortie standard et de la sortie d'erreur standard.

$ find /etc -name passwd 1>listePasswd 2>/dev/null &
$ echo "On peut saisir d'autre commandes en attendant de voir le message ci-dessous une fois le processus terminé"
[1]+  Exit 1                  find /etc -name passwd > listePasswd 2> /dev/null
$ cat listePasswd
/etc/passwd
/etc/webmin/passwd
/etc/pam.d/passwd