Table des matières

Cliquer sur le bouton Table des matières , il y a plein de trucs dans cette page !!

Exemple de script

Recherche d'un mot dans tous les fichiers txt

→ Cherche dans tous les fichiers txt le mot “Agitateur” :
Affiche le(s) nom de fichier et les lignes avec leurs numéros de ligne (grep -n)

$ find . -iname "*.txt" -exec echo {} ';' -exec grep -n "Agitateur" {} ';'
./Ajout mnemonic.txt
6:%Q210.45	F1_agt_c1_km	EBOOL	"Fond 1 : Commande 103KM5 : Agitateur cuve de stockage C1"	
7:%Q210.46	F1_agt_c2_km	EBOOL	"Fond 1 : Commande 103KM6 : Agitateur cuve de stockage C2"	
8:%Q210.47	F1_agt_c3_km	EBOOL	"Fond 1 : Commande 103KM7 : Agitateur cuve de stockage C3"	
9:%Q210.48	F1_agt_c4_km	EBOOL	"Fond 1 : Commande 104KM8 : Agitateur cuve de stockage C4"	
./Modèle_ST.txt
2:(***** Agitateur cuve stockage 5 13M1  O45.0 *****)

Création de dossier avec l'année via une boucle FOR

for ((i=2017; i<=2070; i++))
do
	mkdir $i
done

Liste des dossiers triés par taille croissante, merci Thierry ;-)

$ du -hs */ | sort -h
6,2M	py35dj18/
17M	py27/
58M	py35dj110/
144M	py35dj110oups/
161M	py35/

Arborescence de dossier avec navigation par flèche haut/bas

$ tree -L 2 --du -h | less

re-merci Thierry ;-)


commandlinefu.com

Site avec des exemples de ligne de commande de dingue !!
http://www.commandlinefu.com/commands/browse

re-re-merci Thierry ;-) ;-)


Prise de notes du MOOC Bash

Fev 2018: MOOC Bash sur http://fun-mooc.fr

Raccourcies clavier en ligne de commande:

Déplacements

  • Ctrl + a : Début de ligne
  • Ctrl + e : Fin de ligne (End)
  • Ctrl + b : Recule d’un caractère (Backward)
  • Ctrl + f : Avance d’un caractère (Forward)
  • Alt + b : Recule d’un mot
  • Alt + f : Avance d’un mot

Couper / Coller

  • Ctrl + k : Couper la ligne du curseur à la fin de la ligne
  • Ctrl + u : Couper la ligne du curseur au début de la ligne
  • Ctrl + w : Couper la chaîne depuis le caractère qui précède le curseur jusqu’au début

du mot (si le curseur est placé à la fin d’un mot, coupe le mot)

  • Alt + Backspace : Coupe un mot en arrière
  • Alt + d : Supprimer un mot en avant
  • Ctrl + y : Coller la ligne

Modification

  • Ctrl + t : inverse la position des deux caractères situés avant le curseur
  • Alt + t : inverse la position des deux mots situés avant le curseur
  • Alt + c : met en majuscule la lettre située sous le curseur et déplace le curseur à la fin du mot
  • Alt + l : met en minuscule toutes les lettres depuis la position du curseur jusqu’à la fin du mot
  • Alt + u : met en majuscule toutes les lettres depuis la position du curseur jusqu’à la fin du mot
  • Ctrl + _ : annule la dernière modification

Historique des commandes

$ history
[...]
 1940  source /home/jc/dev/py/py35/bin/activate
 1941  python manage.py runserver
 1942  ll
 1943  python manage.py makemigrations personal_calendar
 1944  python manage.py runserver
 1945  python manage.py migrate
 1946  python manage.py runserver

Manipulation de l’historique

  • history -c permet d’effacer l’historique
  • history -w nomDeFichier permet de sauver l’historique courant dans nomDeFichier
  • history -r nomDeFichier permet d’ajouter les commandes contenues dans nomDeFichier à l’historique courant.
$ history -c  # permet d’effacer l’historique
$ history -w  # nomDeFichier permet de sauver l’historique courant dans nomDeFichier
$ history -r  # nomDeFichier permet d’ajouter les commandes contenues dans nomDeFichier à l’historique courant.

Les commandes avec !

  • !! rappel la dernière commande
  • !hi rappel la dernière commande qui commence par hi
  • !?tor rappel la dernière commande qui contient la chaîne demandée
  • !1946 rapel la commande avec le num. donné par history

Ctrl + R

(reverse-i-search)'sour': source /home/jc/dev/py/py35/bin/activate

Réutiliser les arguments de la dernière commande

  • !* correspond à tous les arguments,
  • !^ au premier argument et
  • !$ au dernier argument de la dernière commande.

Modifier les arguments Syntaxe :

$ !!:s/clé1/clé2/

Exemple :

$ wc fichier1.txt
    9    443    3024 fichier1.txt
$ !!:s/1/2/
wc fichier2.txt
    7    322    2155 fichier2.txt

Caratères spéciaux

  • . Répertoire courant
  • .. Répertoire parent
  • ~ Répertoire personnel de l'utilisateur
  • * Remplace une séquence de caractères
  • ? Remplace un caractère
  • ls [aef]* Liste tous les fichiers commençant par a, e ou f
  • ls [a-m]+ Idem avec interval de caractères

Constructions syntaxiques

Les variables

  • $variable : Forme simplifiée
  • ${variable} : Forme complète
$ i=1
$ mon_cv=Documents/cv
$ echo $i
$ cd $mon_cv
$ echo :qqchose:
::

La substitution de commande

  • Capturer la sortie d'une commande
$(commande)
# exemple:
$ echo ici:$(pwd)
ici:/home/alice
  • Avec affectation de variable
$ rep=$(pwd)
$ echo $rep
/home/alice
  • Avec un fichier texte
$ names=$(cat lipsum.txt)
$ echo $names
Lorem ipsum dolor bla bla bla ...
  • exécution d'une variable avec une commande dedans
$ cmd_ptr="ls -l /bin/z*" #compo. de la Cde dans la variable
$ eval $cmd_ptr           #exécution de la commande
-rwxr-xr-x 1 root root 1937 oct.  27  2014 /bin/zcat
-rwxr-xr-x 1 root root 1777 oct.  27  2014 /bin/zcmp
-rwxr-xr-x 1 root root 5764 oct.  27  2014 /bin/zdiff
-rwxr-xr-x 1 root root  140 oct.  27  2014 /bin/zegrep
-rwxr-xr-x 1 root root  140 oct.  27  2014 /bin/zfgrep
-rwxr-xr-x 1 root root 2131 oct.  27  2014 /bin/zforce
-rwxr-xr-x 1 root root 5938 oct.  27  2014 /bin/zgrep
-rwxr-xr-x 1 root root 2037 oct.  27  2014 /bin/zless
-rwxr-xr-x 1 root root 1910 oct.  27  2014 /bin/zmore
-rwxr-xr-x 1 root root 5047 oct.  27  2014 /bin/znew

Les inhibitions

  • Inhibition totale: Entre 'simple guillemet'
  • Inhibition partielle : Entre “double guillemet”
  • Inhibition caractère: anti-slash \

Exemples:

# inhibition totale:
$ echo '* $(pwd)'
* $(pwd)
 
# inhibition partielle:
$ a=*
$ echo "$a $(pwd)"
* /home/alice
$ echo ":    : $a"    # ici on teste avec des espaces
:    : *
 
# inhibition caractère: anti-slash
$ echo \$a
$a
$ echo - \ -
-  -
$ echo -  -
- -

Contrôler l'exécution des commandes

Retour d'erreur de processus : echo $?

$ ls fichier1.txt  # qui existe
$ echo $?
0
$ ls fichier1.txt  # qui existe
ls: fichier3.txt: No such file or directory
$ echo $?
1

Principaux attributs d'un processus

  • UID: User Id
  • PID: Identifiant du processus
  • PPID: PID du parent
  • CMD: Commande, options, arguments

La commande ps -f

$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD                                       
alice       85     1  0  1902 ttyS0    15049-12:58:15 -bash                               
alice      156    85  0  1902 ttyS0    15049-12:58:15 ps -f                               
$

Les tâches d'exécution

  • Lancer en arrière-plan: &
  • Lister les tâches: jobs
  • identification: %1, %2, …
  • changement de plan: fb, bg
  • suspendre le processus: Ctrl Z

Arret d'exécution

$ sleep 1000 &
[1] 950
$ kill -15 950
[1]+  Terminated    sleep 1000

ou dans la liste de jobs :

$ jobs                                                                                    
[1]   Running                 macmd &                                                     
[2]   Running                 macmd &                                                     
[3]   Running                 macmd &                                                     
[4]+  Stopped                 bash -r                                                     
[5]-  Running                 sleep 600 &                                                 
$ kill %3                                                                                 
$ jobs                                                                                    
[1]   Running                 macmd &                                                     
[2]   Running                 macmd &                                                     
[3]   Terminated              macmd                                                       
[4]+  Stopped                 bash -r                                                     
[5]-  Running                 sleep 600 &                                                 
$ jobs                                                                                    
[1]   Running                 macmd &                                                     
[2]   Running                 macmd &                                                     
[4]+  Stopped                 bash -r                                                     
[5]-  Running                 sleep 600 &                                                 
$

Arrêt groupé: Killall

$ jobs                                                                                    
[1]   Running                 macmd &                                                     
[2]-  Running                 macmd &                                                     
[6]+  Running                 macmd &                                                     
$ killall macmd                                                                           
$                                                                                         
[1]   Terminated              macmd                                                       
[2]-  Terminated              macmd                                                       
[6]+  Terminated              macmd                                                       
$ jobs                                                                                    
$ 

Entrées et sorties des processus

Combiner des commandes séquentiellement

  • Sans lien, sur une même ligne: Caractère ' ; '
  • Par fichier interposé: redirection
  • Par branchement: le pipe

E/S standard: stdin, stdout, stderr

  • num 0 stdin
  • num 1 stdout
  • num 2 stderr

Les redirections

Sortie standard: 2redirections

  • simple: >
  • en ajout: »

redirection vers un dispositif:

  • “trou noir” : >/dev/null

Sortie des erreurs

  • simple: 2>
  • en ajout: 2»

Entrées standard:

  • Redirection standard: <

Exemple avec wc (word count)

$ wc -l < fichier1.txt    # en redirection
3
$ wc -l  fichier1.txt     # utilisation normale
3 fichier1.txt

Branchement de commandes

  • Le caractère tube (pipe): '|'

Exemple: comtage du nombre de ligne donné par history:

$ history | wc -l
5547

Contrôler son environnement

Environnement de travail: printenv, set, unset, export, declare

  • printenv
$ printenv                                                                                
SDL_MOUSEDRV=TSLIB                                                                        
DISPLAY=:0                                                                                
ps24=3                                                                                    
SDL_MOUSEDEV=/dev/input/event1                                                            
USER=alice                                                                                
PWD=/home/alice/Sequence2/A24                                                             
MANPAGER=less -R                                                                          
HOME=/home/alice                                                                          
TSLIB_TSDEVICE=/dev/input/event1                                                          
SHELL=/usr/bin/bash                                                                       
TERM=linux                                                                                
SHLVL=1                                                                                   
MANPATH=/usr/share/man                                                                    
LOGNAME=alice                                                                             
PATH=/bin:/usr/bin:.:/usr/script                                                          
PS1=\[\e[32m\]\$\[\e[m\]                                                                  
HISTSIZE=10000                                                                            
HISTFILESIZE=10000                                                                        
_=/bin/printenv                                                                           
OLDPWD=/home/alice                                                                        
$
  • set

La commande set comme la commande shopt permet aussi de modifier les options du shell. La commande set -o permet de lister les options contrôlées par la commande set. Pour obtenir de l’aide sur cette commande : help -m set.

$ set
[...]
OPTERR=1                                                                                  
OPTIND=1                                                                                  
OSTYPE=linux-musl                                                                         
PATH=/bin:/usr/bin:.:/usr/script                                                          
PIPESTATUS=([0]="0")                                                                      
PPID=1                                                                                    
PS1='\[\e[32m\]\$\[\e[m\] '                                                               
PS2='> '                                                                                  
PS4='+ '                                                                                  
PWD=/home/alice/Sequence2/A24                                                             
SDL_MOUSEDEV=/dev/input/event1                                                            
SDL_MOUSEDRV=TSLIB                                                                        
SHELL=/usr/bin/bash                                                                       
SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor       
SHLVL=1                                                                                   
TERM=linux                                                                                
TSLIB_TSDEVICE=/dev/input/event1                                                          
UID=1000                                                                                  
USER=alice                                                                                
_=set                                                                                     
blue='\e[1;34m'                                                                           
mystere23='wc -c /home/alice/Sequence2/A25/lipsum.txt'                                    
p1=mystere                                                                                
p2=23                                                                                     
ps24=3                                                                                    
$                                                                                         
  • Variables personnalisée: ~/.bashrc

Ajouter la variable avec vi:

D=/home/alice/Documents/cv
export D 

Actualiser le bash:

$ source .bashrc
  • unset

Permet de supprimer une variable

$
$
1
$
$
var=1
echo $var
unset var
echo $var
$
  • declare

La commande declare sans paramètre affiche comme la commande set toutes les variables de la session courante. La seule différence est que la commande declare différencie les variables exportées.

  • export

Une variable créée par l’utilisateur dans un shell est dite locale. Elle n’est connue qu’a l’intérieur du shell qui l’a créée. Il peut arriver que l’on souhaite lancer un autre shell depuis le shell actuel. On appelle ce second shell un sous-shell. Les variables d’environnement du shell original sont “transmises” au sous-shell. Ainsi, si dans le shell d’origine la variable d’environnement HOME existe et est définie, dans le sous-shell cette variable existera aussi et aura la même valeur.

$ printenv
[...]
HOME=/home/mylogin
[...]
$ bash
$ printenv
[...]
HOME=/home/mylogin
[...]

Dans l’exemple ci-dessus, la deuxième commande bash lance le programme Bash dans le shell original. On vérifie que dans ce sous-shell, la variable d’environnement HOME est bien définie et a la même valeur que le shell original.
Exemple :

$ mytimezone=CET
$ echo $mytimezone
CET
$ bash
$ echo $mytimezone

Ci-dessus, la variable mytimezone n’est pas accessible au sous-shell.

$ mytimezone=CET
$ echo $mytimezone
CET
$ export mytimezone
$ printenv
[...]
mytimezone=CET
[...]
$ bash
$ echo $mytimezone
CET

Les fichiers de configuration du shell

  • /etc/profile

Le fichier /etc/profile est un script exécuté automatiquement à la connexion pour tous les utilisateurs. Ce fichier définit les variables et paramètres universels identiques pour tous les utilisateurs. Attention, ce fichier est commun à plusieurs shells (sh, ksh, bsh, bash, · · · ). Il ne faut donc pas y mettre de variables spécifiques à Bash. Et si de telles variables spécifiques sont nécessaires il faut les mettre dans le fichier /etc/bashrc. L’exemple suivant montre un extrait de ce que peut contenir le fichier /etc/profile.

[...]
PATH="$PATH:/usr/local/bin:/usr/bin"
USER=‘id -un‘
LOGNAME=$USER
export PATH USER LOGNAME
[...]

Cet exemple montre que les variables d’environnement PATH, USER, LOGNAME sont définies et exportées en tant que variables d’environnement.

  • ~/.profile

Le fichier ~/.profile ou /home/mylogin/.profile contrairement au fichier /etc/profile n’est exécuté que lorsque l’utilisateur mylogin se connecte. Ce fichier est exécuté après l’exécution du fichier /etc/profile - il permet donc de modifier les variables déjà définies dans /etc/profile ou d’en créer de nouvelles. Il est modifiable par l’utilisateur et contient les variables d’environnement qui lui sont spécifiques. C’est dans ce fichier par exemple que doit être créée puis exportée notre variable mytimezone. Pour ce faire, on doit ajouter les lignes suivantes dans ce fichier :

[...]
mytimezone=CET
export mytimezone
[...]
  • Les fichiers /etc/bashrc et /.bashrc

Si le shell de l’utilisateur mylogin est Bash alors le script /home/mylogin/.bashrc est exécuté à la suite des fichiers /etc/profile et /home/mylogin/.profile. Le fichier /home/mylogin/.bashrc débute souvent par des commandes qui vont conduire à lancer l’exécution du fichier /etc/bashrc. Car ce dernier contient des variables, fonctions, ou alias propres à Bash et communs à tous les utilisateurs. La suite du fichier /home/mylogin/.bashrc et complétée par les définitions des variables, fonctions et alias spécifiques à l’utilisateur. Voici un extrait du contenu d’un fichier /home/mylogin/.bashrc

[...]
if [ -f "/etc/bashrc" ] ; then
source /etc/bashrc
fi
export EDITOR=/usr/bin/vi
[...]

Les premières lignes testent si le fichier /etc/bashrc existe, et si c’est le cas l’exécutent avec la commande source. Puis, la variable EDITOR est définit et exporte. La valeur de cette variable est la commande de l’éditeur de texte par défaut de l’utilisateur.

L’invite de commande (prompt) et le terminal

  • La variable PS1

Exemples de prompt :

$ PS1="\u > "
alice >
alice > PS1="\u dans \w > "
alice dans ~ > cd /tmp
alice dans /tmp >

Les commandes pouvant être utilisées pour personnaliser le prompt :

\u    login de l’utilisateur courant
\w    répertoire courant, avec $HOME abrégé par un tilde
\t    Heure courante au format 24-heures HH:MM:SS
\s    Le nom du shell, aussi retourné par la variable $0
\T    Heure courante au format in 12-heures HH:MM:SS
\@    Heure courante au format in 12-heures AM/PM
\A    Heure courante au format in 24-heures HH:MM
\H    Nom de la machine
\W    Nom de base du répertoire courant, avec $HOME abrégé par un tilde
\!    Le numéro historique de la commande
/#    Le Numéro de la commande
\n    Passage à la ligne

Découpage de fichier head, tail, split

  • head
  • tail

Exemples head et tail:

$ head .bash_history
[...] #10 première lignes du fichier par défaut
$ tail .bash_history
[...] #10 dernière lignes du fichier par défaut
$ tail -5 .bash_history    #5 première lignes
  • split

La commande split permet de découper un fichier en plusieurs morceaux.
L’exemple suivant permet de diviser le fichier /var/log/syslog en plusieurs fichiers de 22 octets, avec l’option -b 22, qui seront appelés : log_aa, log_ab, ….

$ split -b 22 /var/log/syslog log_

La commande suivante effectue la même division mais en séparant en fichiers ayant le même nombre de lignes, dans notre cas 30, avec l’option -l 30.

split -l 30 /var/log/syslog log_

Méta-information: wc

$ wc -l .bash_history
5448  .bash_history

Extraire une colonne: cut

Exemples cut:

$ cut -f 1 -d " " .bash_history
[...]
ls
cd
vi
-f 1: champ 1
d “ ”: délimiteur espace

Ordonner une sortie: sort, uniq, cut

Les commande sort et uniq:

$ cut -f 1 -d " " .bash_history > /tmp/c
$ sort /tmp/c > /tmp/d
$ uniq -c /tmp/d
[...]
1 whoami
3 whois
1 zsh

Connaître les 10 commandes les plus utilisées :

$ uniq -c /tmp/d | sort -nr | head
1161 ls
 748 cd
 333 cat
 254 vim
[...]
uniq -c: c=count
sort -nr: n=numeric r=reverse (inverse le tri)
  • Evaluation du MOOC avec sort:

Trier dans ordre croissant des notes données aux actrices (colonne 3)

$ cat elle/notes/actress.csv | sort -t : -n -k 3
[...]
Ryder:Winona:89                                                                           
Berry:Halle:90                                                                            
Swanson:Gloria:91                                                                         
Wood:Ashley:91                                                                            
Davis:Geena:99                                                                            
Gillan:Karen:99                                                                           
Weisz:Rachel:99                                                                           
sort -t : -n -k 3
  • -t séparateur :
  • -n numérique
  • -k 3 num colonne csv en partant de 1
  • Evaluation du MOOC avec cut:

Extraire les noms et prénoms du CSV

$ cat elle/notes/actress.csv | cut -d ":" -f 1,2
[...]
Kelly:Grace                                                                               
Swank:Hillary                                                                             
Goldberg:Whoopie                                                                          
Davis:Geena                                                                               
Zellweger:Renee                                                                           
Gillan:Karen                                                                              
$

paste

Contrairement à la commande cut qui permet d’extraire des informations d’un fichier ou de l’entrée standard, la commande paste permet de fusionner les colonnes contenues dans plusieurs fichiers.
La commande paste permet de réunir les deux fichiers comme le montre l’exemple suivant. Attention, l’ordre dans lequel les fichiers sont donnés en paramètre est important.

$ paste low.txt high.txt
1    4
2    5
3    6

L’option -d permet de modifier le caractère de séparation qui par défaut est la tabulation.

$ paste -d : low.txt high.txt
1:4
2:5
3:6

join

La commande join est similaire à la commande paste car elle permet de mixer plusieurs informations provenant de fichiers ou de l’entrée standard. La commande join permet de joindre des fichiers en fonction de mots clés. Prenons l’exemple suivant :

$ cat nom.txt
BROOKS Belle xx
JACK Alice 10
DANIEL Beatrice 1
 
$ cat niveau.txt
Alice bash guru
Belle unknown status
Beatrice bash newbie

Pour lier les deux fichiers en fonctions des prénoms, on utilise la commande suivante :

$ join -1 2 -2 1 nom.txt niveau.txt
Alice JACK 10 bash guru
Belle BROOKS xx unknown status
Beatrice DANIEL 1 bash newbie

L’option -1 2 signifie que dans le premier fichier, soit dans notre cas nom.txt, le champ définissant la relation est la deuxième colonne. De même l’option -2 1 signifie que dans le second fichier, dans notre cas niveau.tItaliquext, le champ définissant la relation est la première colonne. Attention au fait que dans la sortie de la commande, chaque ligne commence par la clef de relation.

Trouver et appliquer une commande sur des fichiers: find, grep, sed

find -exec

  • find
$ find . -iname "*.mkv" -size +1G -mtime -7 -o -iname "*.epub" -user alice -mtime -7
tralala
tralala
  • find + exec
$ find . -iname "*.mkv" -size +1G -mtime -7 -exec mv {} /tmp/. ';' 
        -o -iname "*.epub" -user alice -mtime -7 -exec mv {} /tmp/. ';' 

Quelques paramètres de find :

  • -name fichierAtrouver
  • -iname fichieràtrouver (sans casse)
  • -perm nombre où nombre équivaut aux droits d’accès.
  • -user nom où nom correspond à un nom d’utilisateur et le résultat de la recherche listera tous les fichiers appartenant à cet utilisateur.
  • -size n où n correspond à la taille du fichier
  • -exec cmd où cmd correspond à une commande à exécuter sur chacun des fichiers trouvés.
  • -mtime n où n correspond à une date ou un temps.

Exmples :

$ find /etc /bin -name "a*"
$ find . -iname "d*" -type d # d pour les répertoires, l pour les liens symboliques et f pour les fichiers.
#Liste toutes les entités du répertoire personnel qui ont été modifiées il y a moins de 7 jours.
$ find $HOME -mtime -7 
#Liste toutes les entités du répertoire courant dont la taille dépasse le méga octet
$ find . -size +1M
#Liste toutes les entités de votre répertoire personnel dont vous n’êtes pas le  propriétaire, car l’option ! exprime la négation.
$ find $HOME ! -user $LOGNAME

ET et OU avec la commande find

#Liste toutes les entités du répertoire courant dont la taille dépasse cinq méga octets mais dont la taille est aussi inférieure à un giga octet.
$ find . -size +15M -size -1G
#L’option -o de la commande find permet de chercher sur des unions de critères (OU).
#Liste toutes les entités du répertoire courant dont le nom se termine par "*.py" ou par "*.cpp".
$ find . -name "*.py" -o -name "*.cpp"

L’option -exec

$ find . -iname "*.jpg" -exec echo "AVANT" {} "APRES" ';'

Tous les {} sont remplacés par le chemin trouvé en protégeant automatiquement les espaces.
On peut enchaîner des actions avec plusieurs -exec et utiliser plusieurs fois {} dans la même commande :

$ find . -iname "*.txt" -exec wc -l {} ’;’ -exec cp {} {}.bak ’;’

→ Trouve toutes les entités dont le nom se termine par “*.txt”, puis exécute la commande wc -l sur les entités trouvées (représentée par {}) et ensuite copie les entités trouvées dans des fichiers de même nom avec l’extension {}.bak.

grep

  • Dans bash_history, recherche des lignes qui commencent par “mpl” et ce terminent par “mkv”
$ grep "^mpl.*mkv$" .bash_history
tralala
tralala

Les options de la commande les plus utilisées sont

  • -v pour inverser la selection,
  • -n pour afficher le numéro de ligne et
  • -c pour donner le nombre de lignes trouvées.
# compter le nb de ligne qui ne contiennent pas UNIX:
$ grep -vc UNIX slogan.txt
7

Les motifs de recherche utilisés par la commande grep sont les expressions régulières.
Exemple:

# début de ligne :
$ grep ^Li slogan.txt
Linux inside
Live free or die UNIX
 
# Fin de ligne
$ grep ux$ slogan.txt
Proudly powered by linux
Kids are stronger with linux
If you really want to know, look into linux
 
# Commence par K ou U:
$ grep ’^[KU]’ slogan.txt
UNIX or nothing
Kids are stronger with linux

Options courantes de la commande grep

  • -i retire la sensibilité à la casse
  • -w permet de rechercher la présence d’un mot entier (délimité par des espaces ou situé en début ou fin de phrase).
  • Un pipe | permet de rechercher plusieurs mots. $ grep “UNIX\|Linux” slogan.txt
  • Gestion des lignes: -B before
  • Gestion des lignes: -A after
  • Gestion des lignes: -C center

Exemples gestion de lignes :

# 2 lignes avant, 3 lignes après:
$ grep -B2 -A3 "nothing" slogan.txt
# centré: 2 lignes avant/après
$ grep -C2 "nothing" slogan.txt

Exemples d’expressions régulières

  • . représente un caractère quelconque.
  • [A-Z] Majuscule entre A et Z
$ grep "[A-Z].[v]" slogan.txt
Give that man a Bash
Live free or die UNIX
  • Les accolades dans les expressions régulières permettent de spécifier des répétitions.

Exemple : On recherche les lignes contenant une lettre majuscule entre “R” et “Z”, puis 4 caractères minuscules entre “a” et “z” puis un espace. Ici le mot correspondant est “There”. Notez le caractère d’échappement \ avant l’accolade ouvrante et fermante.

$ grep[R-Z][a-z]\{4\} ’ slogan.txt
There is nothing UNIX can’t buy

La commande awk

awk n’est pas à proprement parler une commande ou un filtre mais plutôt un langage de programmation. On le classe parmi les filtres programmables. awk est un outil de sélection et de manipulation de texte comme la commande grep. La syntaxe d’utilisation de la commande awk se présente sous la forme suivante :

$ awk ’commandes awk’ fichier

awk traite les lignes du fichier une à une et de manière séquentielle. Dans awk, chaque ligne contient un certain nombre de champs séparés par un séparateur qui par défaut est un espace. Ce séparateur peut être modifié avec l’option -F de awk, ou encore en changeant la valeur de la variable FS interne à awk. Cette variable est modifiable dans la partie ’commandes awk’ de la syntaxe ci-dessus.
Dans tout ce qui suit, les variables qui vont apparaître dans la partie ’commandes awk’ sont des variables internes à awk. C’est à dire que ce ne sont pas des variables accessibles depuis le shell, elles ont seulement un sens bien spécifique dans les traitements réalisés par awk. Pour awk une ligne complète est contenu dans la variable $0 et ses différents champs sont $1, $2, $3, … numérotés de gauche à droite. Considérons le fichier wcs.txt suivant :

$ cat wcs.txt
Ada LOVELACE 1815 1852
Annie EASLEY 1933 2011
Grace HOPPER 1906 1992

La commande suivante permet d’inverser l’ordre prénom-nom en nom-prénom dans le fichier wcs.txt :

$ awk{print $2 " " $1 " " $3 " " $4}’ wcs.txt
LOVELACE Ada 1815 1852
EASLEY Annie 1933 2011
HOPPER Grace 1906 1992

Dans cette commande, les espaces entre les champs doivent être explicitement exprimés avec “ ”. Le mot clé print est un mot clé spécifique à awk qui permet d’afficher les variables. Comme spécifié précédemment, awk est un langage de programmation très riche, et nous n’entrerons pas dans les détails de celui-ci. Il permet de faire des calculs arithmétiques, de réaliser des tests logiques et contient de nombreuses fonctions (dont les fonctions mathématiques courantes).
Par exemple, awk permet facilement d’effectuer des calculs sur les champs. L’exemple suivant montre le calcul de l’âge et l’ajout d’un texte au moment de l’affichage du résultat. L’exemple modifie aussi le séparateur entre l’année de naissance et l’année du décès.

$ awk{print $2 " " $1 " " $3 "-" $4 " age: " $4 - $3}’ wcs.txt
LOVELACE Ada 1815-1852 age: 37
EASLEY Annie 1933-2011 age: 78
HOPPER Grace 1906-1992 age: 86

awk traite le ou les fichiers ligne par ligne, mais pas seulement. awk permet aussi de spécifier des blocs de pré-traitement et post-traitement. Ces blocs sont exécutés avant le traitement de la première ligne du ou des fichiers, et après le traitement de la dernière ligne. Pour cela, la syntaxe de awk à utiliser est la suivante (un bloc vide peut être omis) :

awk ’ BEGIN { traitements débuts, avant lecture fichier}
           { traitement courants, ligne par ligne}
       END {traitements fins, après parcours fichier} ’ fichier.

L’exemple suivant montre l’utilisation de ces blocs.

awk ’ BEGIN {print "Les informaticiennes :"}
           {print $2 " " $1 " " $3 "-" $4 " age: " $4 - $3}
     END {print "Il y en a : " NR ". On en veut plus."}’ wcs.txt
Les informaticiennes :
LOVELACE Ada 1815-1852 age: 37
EASLEY Annie 1933-2011 age: 78
HOPPER Grace 1906-1992 age: 86
Il y en a : 3. On en veut plus.

Le résultat de l’exemple précédent montre que l’on cherche à afficher, avant le traitement du fichier, la chaîne de caractères Les informaticiennes : présente dans le bloc BEGIN. Ensuite, à la fin du traitement nous avons cherché à afficher Il y en a : “ NR ”. On en veut plus.. La variable NR est une variable prédéfinie de awk qui contient le numéro de la ligne en cours d’analyse. Dans notre cas, puisque l’utilisation de cette variable se fait dans le bloc END, la valeur de NR correspond au numéro de la dernière ligne qui a été analysée. À l’affichage, cette variable est remplacée par sa valeur.

Modifier avec sed

Exemple de base : La commande sed (stream editor) permet de modifier un flux de données de taille illimité en utilisant très peu de mémoire.

$ sed -i '' "/^mpl.*mkv$" .bash_history
$ grep "^mpl.*mkv$" .bash_history
rien !

La commande sed peut utiliser les expressions régulières qui en font un filtre très puissant et dont les possibilités sont infinies. La commande sed lit les fichiers dont les noms sont indiqués en paramètres. Si aucun nom n’est indiqué, elle lit l’entrée standard. On lui indique les traitements à effectuer en utilisant l’option -e (la présence de cette option est facultative s’il n’y a qu’une seule directive de traitement). Par défaut les fichiers indiqués ne sont pas modifiés et le résultat de la transformation est écrit sur la sortie standard. Mais avec l’option -i on peut obtenir que le résultat soit écrit dans chacun des fichiers.

L’utilisation principale de la commande sed est la substitution d’une expression régulière par une chaîne de caractères, la forme syntaxique pour exprimer ce traitement est la suivante :

s/expression-reguliere/chaîne/g

L’option /g indique que toutes lignes validant l’expression régulière seront traitées, sinon le traitement s’arrêtera après la première occurrence trouvée. La commande sed permet d’utiliser les expressions régulières étendues avec l’option -r, nous n’entrerons pas dans ce type d’expressions régulières ici. Pour utiliser les caractères spéciaux tel que / ou & dans les expressions ou la chaîne, il faut utiliser le caractère d’échappement \. Il est possible d’effectuer plusieurs substitutions à la suite, elles seront alors traitées les unes après les autres et de gauche à droite.

Voici les exemples d’utilisation les plus courants :

$ sed ’s/alice/bob/g’ fichier1 fichier2

Lit le contenu des fichiers fichier1 et fichier2 et l’écrit sur la sortie standard en remplaçant tous les alice par bob.

Pour supprimer un mot il suffit de substituer le mot par une chaîne vide. Par exemple, pour supprimer le mot alice la commande est :

$ sed ’s/alice//g’ fichier1 fichier2

Il est possible d’utiliser la commande sed comme filtre :

$ cat fichier | sed -e ’s/a/A/g’ -e ’s/TA/ta/g’

La commande cat affiche le contenu du fichier fichier sur la sortie standard, qui est reprise par la commande sed (c’est le cas classique d’utilisation d’une commande filtre). Notez ici que nous avons utilisé l’option -e car la commande contient plusieurs directives de traitement. La première substitution change tous les a en A et la seconde tous les TA en ta. Ce qui signifie que si fichier contient une ligne a Table , cette ligne sera transformée en A table . En effet, la première substitution transformera :

a Table --> A TAble

et la deuxième substitution agira sur le résultat de la première substitution :

A TAble --> A table

Options classiques de sed

  • Intervalle de ligne 1 à 5 incluses:
$ sed1,5s/UNIX/*nix/g’ slogan.txt
  • il est aussi possible de spécifier les lignes en fonction d’un motif de recherche.

Ex: Permet de remplacer ’Bash’ par ’BASH’ sur les lignes commençant par ’C’.

$ sed -e/^C/s/Bash/BASH/g’ slogan.txt

La commande suivante permet de faire ce changement sur les lignes contenant le mot ’that’.

$ sed -e/that/s/Bash/BASH/g’ slogan.txt
  • Utilisation des expressions régulières complexes.

La commande suivante remplacera toutes les chaînes de caractères commençant par ’L’ et se terminant par ’x’ avec n’importe quel nombre de caractère entre ces deux lettres par “XXX”.

$ sed -e ’s/L.*x/XXX/g’ slogan.txt

En remplaçant “XXX” par un “”, la chaîne de caractères sera supprimée (remplacée par une chaîne vide).

  • sed permet aussi de supprimer des lignes trouvées, au lieu de faire une substitution comme on l’a fait jusqu’ici. La commande suivante permet de supprimer, grâce à l’action /d, toute les lignes commençant par L et se terminant par e.
$ sed -i ’’ -e/^L*e$/d’ slogan.txt

L’option -i ” (avec les deux quotes) permet d’effectuer les modifications directement dans le fichier slogan.txt. Attention selon les versions de la commande sed l’option est -i ” (pour la famille Linux, GNU) ou -i (pour la famille Unix, BSD). Un autre exemple simple est la suppression de toute les lignes contenant le mot Linux.

$ sed -e/Linux/d’ slogan.txt

Options puissantes

  • La commande suivante permet de préfixer chaque ligne par “Je dis : ”.

Dans cet exemple le caractère & indique à la commande sed d’insérer la chaîne de caractère trouvée par l’expression régulière. Dans notre cas .* permet de sélectionner toute une ligne

$ sed -e ’s/.*/Je dis: &/’ slogan.txt
  • Utilisation des régions :
$ cat date.txt
2017-01-21
2019-11-09
2012-08-17
2003-15-13

→ Les régions (.*)-(.*)-(.*)

$ sed -e ’s/\(.*\)-\(.*\)-\(.*\)/date: \3 \2 \1/’ date.txt
date: 21 01 2017
date: 09 11 2019
date: 17 08 2012
date: 13 15 2003

Exemple du Challenge A33:
Donner le nombre d'actrices différentes notées par tous les magazines :

$ find -name *.csv -exec cat {} ';' | cut -d ":" -f 1,2 | sort | uniq | wc -l 

Effectuer des calculs

expr: Entiers arg

  • Caractères et opérateurs sont passés en arguments
$ expr 1 + 2
3
  • Avec une variable :
$ a=$(expr 1 + 2)
$ echo $a
3
  • Variable indéfinie et inhibition partielle
# i est indéfinie.
$ i=$(expr $i + 1)
expr: syntax error
$ i=$(expr "$i" + 1)
$ echo $i
1
  • Attention aux priorités
$ expr 10 + 6 / 2
13
$ expr \( 10 + 6 \) / 2
8
Opérateur Libellé
\( expression \) parenthésage
\* multiplication
/ division entière
% reste de la division entière
+ addition
- soustraction
Spécificité de expr :
  1. Les éléments d'expressions sont les arguments séparé par espace
  2. Expansions et substitutions faites par le shell :
    • Positif: utilisation des variables
    • Négatif: les caractères spéciaux sont interprété par le shell avant la commande
$ expr $a \* 3
6

Substitution arithmétique: langage C

$((expression))
$ a=2
$ a=$((a*3))
$ echo $a
6
Le résultat de l'expression est interprété par le shell comme une commande :
$ $((i=1+2))
-bash: 3: command not found
$ echo $i
3
Avantages :
  1. pas espace séparateur
  2. caractères spéciaux du shell ne sont pas inhibé
  3. pas de $ sur la variable
  4. La commande est interne au shell. Elle s’exécute dans le même processus que le shell lui-même.

Mais attention :

  • la substitution arithmétique ne s'utilise pas comme une commande
  • mais comme l'argument d'une commande.
Opérateurs Opérateurs
( ) parenthésage
++ – incrément, décrément
! négation logique
double étoile puissance
*/% multiplication, division, reste
+ - addition, soustraction
< = >= < > == != comparaison
&& et logique
double pipe ou logique
= *= /= %= -= += affectation

La commande interne let (scripts)

  • Forme complète :
$ a=2
$ let "a=a+1"
$ echo $a
3
  • Forme abrégée: double parenthèsage
$ ((a=a+1))
$ echo $a
4
  • avec le langage C
la commande let n’effectue aucune sortie.

Exemples d'utilisation :

$ ((a++))
$ echo $a
5

Variantes :

$ let "a=a+1"
$ let "a+=1"
$ ((a++))

Exemple d'erreur :

$ let a=a * 2
-bash: let: *: syntax error
$ let "a=a * 2"

Compatibilité avec expr :

$ a=10
$ ((a = \( $a + 20 \) / 2 ))
$ echo $a
15
Opérateurs Opérateurs
( ) parenthésage
++ – incrément, décrément
! négation logique
double étoile puissance
*/% multiplication, division, reste
+ - addition, soustraction
< = >= < > == != comparaison
&& et logique
double pipe ou logique
= *= /= %= -= += affectation
Avantages :
  1. pas espace séparateur
  2. caractères spéciaux du shell ne sont pas inhibé
  3. pas de $ sur la variable
  4. La commande est interne au shell. Elle s’exécute dans le même processus que le shell lui-même.

Déclaration var typée

$ declare -i b=10
$ b=(b+8)/2
$ echo $b
9

bc : Basic Calculator, les décimaux (scripts)

  • bc en mode interpréteur
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'. 
scale=10
10/3
3.3333333333
quit
$
  • bc en mode commande comme filtre
$ echo "scale=10; 10/3" | bc
3.3333333333
  • avec une variable
$ c=$(echo "scale=10; 10/3" | bc)
$ echo $c
3.3333333333

Calculs numériques: Conseils

tar: Archiver et compresser des données

Les options les plus courantes de la commande tar sont :

  • Création d’une archive :
$ tar cvf nom_a_creer.tar chemin_repertoire_existant
  • Extraction d’une archive :
$ tar xvf nom_de_l_archive.tar
  • Lister le contenu d’une archive :
$ tar tvf nom_de_l_archive.tar
  • Extraire seulement une partie d’une archive.
$ tar xvf nom_de_l_archive.tar rep

La compression et la transformation de données

La commande gzip

La commande gzip (GNU zip) permet de compresser des fichiers pour réduire la place qu’ils prennent sur le disque. Cette compression est souvent utilisée avant le transfert du fichier à travers un réseau de communication pour réduire le temps de transfert. La commande gzip s’utilise de la manière suivante :

$ gzip monFichier

Le résultat de cette commande est la création d’un fichier monFichier.gz et la suppression du fichier monFichier. Il est possible de donner plusieurs noms de fichiers ou de compresser récursivement un répertoire sans en charger la hiérarchie.

La commande gunzip

La commande gunzip (GNU unzip) permet de décompresser des fichiers d’extension .gz.

$ gunzip monFichier.gz

Va créer un fichier monFichier et détruire le fichier monFichier.gz.
Pour conserver le fichier original :

$ gunzip -c monFichier.gz > monFichier

tar et gzip combinés

La commande gzip est aussi une option de la commande tar. Ainsi dans la commande tar czvf f.tgz f/ l’option z permet de compresser directement l’archive créée. Dans notre cas, l’archive du répertoire f/ sera compressée et le résultat se trouvera dans le fichier f.tgz. L’extraction se passe de la même manière avec l’option x.

Scripts

Les arguments

$1 -> arg1
$2 -> arg2
$3 -> arg3
 
$# -> 3    # nombre d'arguments
 
$@ -> { arg1 arg2 arg3 }    # liste des arguments

Prudence vis à vis du contenu des variables

Pas de problème hors script :

$ TEXTE="Bonjour tous"
$ echo $TEXTE
Bonjour tous
$

Avec un script :

$ TEXTE="Bonjour tous"
$ ./ngArg.sh $TEXTE
Il y a 2 argument(s)
Le 1er est : Bonjour
$

il faut utiliser l’inhibition partielle de la variable.

$ TEXTE="Bonjour tous"
$ ./ngArg.sh "$TEXTE"
#le script ngArg
Il y a 1 argument(s)
Le 1er est : Bonjour tous
$

exit: Code de retour de script

Pour indiquer un code de retour d'un script

exit 0    # ok
exit 1    # erreur

Syntaxes avancées des variables

${nom_variable:-valeur}   # valeur par défaut pour un argument
${nom_variable:?message}  # val var ou message err et stop script
-> Contrôle argument obligatoire.

Exemples pas d'argument obligatoire: nbArg.sh

#!/bin/bash
 
echo "Il y a $# argument(s)"
FIRST=${1:-vide}
echo "Le 1er est : $FIRST"

test :

$ ./nbArg.sh
Il y a 0 argument(s)
Le 1er est : vide

Exemples avec un argument obligatoire: arg1.sh

#!/bin/bash
 
ARG1=${1:?"Vous devez fournir un argument"}
echo "Le 1er est : $ARG1"

test :

$ ./arg1.sh
./arg1.sh line 3: 1: Vous devez fournir un argument
$

La substitution de variable s’écrit dans sa forme complète avec des accolades : ${variable} La forme complète s’utilise quand le caractère qui suit le nom de la variable est un caractère alphanumérique ou le caractère souligné. Ainsi si dans notre script henri.sh nous devons faire un pseudonyme composé du nom et de l’âge, séparés par la lettre A, nous allons avoir besoin d’utiliser la forme complète. Voyons ce que cela donne :

#!/bin/bash
AGE=45
NOM=Henri
PSEUDO1="$NOMA$AGE"
echo $PSEUDO1          # -> 45
PSEUDO2="${NOM}A$AGE"  # -> HenriA45
echo $PSEUDO2

La portée des variables

Dans un script on dit que les variables sont locales car les affectations de variables qu’on réalise ne vont pas se répercuter dans le contexte du processus qui a lancé l’exécution du script.

$ ./henri.sh
45
HenriA45
$ echo $PSEUDO1
 
$

"Sourcer" un script

Il existe un moyen de contourner ce comportement de localité des variables manipulées au sein d’un script. Il faut pour cela lancer l’exécution du script dans le même contexte d’exécution que le shell du terminal. Ceci se fait à l’aide de la commande source qui se note de manière abrégée par le symbole point (’.’) devant le nom du script à exécuter. On dit alors qu’on “source” le script.

$ source ./henri.sh
45
HenriA45
$ echo $PSEUDO1
45
$

Utiliser des variables externes

On met en commentaire l'affectation de AGE.

#!/bin/bash
#AGE=45
NOM=Henri
[...]

On cré la variable AGE=30

$ AGE=30
$ ./henri.sh
HenriA
 
$

Cela est une nouvelle fois dû au fait que le shell du terminal et le script sont exécutés dans 2 processus différents et donc dans 2 contextes d’exécution différents. Il faut exporter la variable :

$ export AGE=30
$ ./henri.sh
30
HenriA30
$

Débogage d’un script

Il est possible d’afficher chacune des commandes exécutées par un script à l’aide de l’option d’exécution -x du Bash :

$ bash -x script_a_lancer

Chaque commande exécutée sera alors affichée dans la console sur une ligne qui commencera par le caractère ’+’. On peut aussi demander l’affichage des lignes et des blocs lus avant leur traitement en ajoutant l’option –v :

$ bash -x -v script

Enfin, avant d’exécuter un script, il peut déjà être intéressant de savoir si la syntaxe du Bash a été respectée. Dans ce cas, l’option -n va vous permettre de lever les erreurs de syntaxe ou de vérifier l’absence d’erreur.

$ bash -n script

Les tests: nombres, chaînes, fichiers

Nombres

2 écritures:

  • test expression
  • [espace expression espace]

Code retour:

  • VRAI → 0
  • FAUX → 1

Les opérateurs de comparaison arithmétique:

-eqequalégal à
-nenon equaldifférent de
-gtgreather thanstrictement supérieur à
-gegreather or equalsupérieur ou égal à
-ltless thanstrictement infértieur
-leless or equalinférieur ou égal à

Exemple :

$ [ 3 -gt 12 ]; echo $?
1

Chaînes de caractères

Opérateurs binaires :

  • = égal à, on peut aussi utiliser ==
  • != n'est pas égale à
  • \< inférieur à
  • \> supérieur à

Opérateurs unaires :

  • -z test si une chaîne est vide
  • -n teste si une chaîne n'est pas vide
$ mot1=alice
$ [ $mot1 = alice ]; echo $?
0
$ [ $mot1 \> bob ]; echo $?
1

Fichiers

  • -e existe-t-il
  • -s est-il vide
  • -f est-ce un fichier ordinaire
  • -d est-ce un répertoire
  • -r est-il accessible en lecture
  • -w est-il accessible en écriture
  • -x est-il exécutable

Les opérateurs logiques

  • ! NON: inverse le code retour de l'expression de test
  • -a ET: vrai si les 2 expressions sont vraies
  • -o OU: vrai si l'une des 2 expressions est vraie
# Vérifions que le fichier confidentiel n'est PAS accessible en lecture
$ [ ! -r confidentiel ]; echo $?
0
$ [ ! -r confidentiel -a ! confidentiel ]; echo $?
1
$ [ ! -r confidentiel -a ! confidentiel -o ! -e confidentiel ]; echo $?
0

Challenge: Fichier protégé

Ce que j'ai fais et qui fonctionne:

#!/bin/bash
 
if [ $# -ne 0 ] 
then
    if [ -f $1 -a ! -w $1 ]
    then
        exit 0
    else
        exit 1
    fi
else
  exit 1
fi

Ce qu'il fallait faire LOL

### fichier exo2.sh ###
#!/bin/bash
[ -f "$1" -a ! -w "$1" ]

test étendu [[ ... ]]

Cette commande apporte quelques facilités par rapport à la commande test :

  • on peut utiliser les opérateurs < et > sans les faire précéder du caractère d’inhibition antislash \
$ [[ "bateau" < "navire" ]]; echo $?
0
  • on peut utiliser les opérateurs logique && (ET) et || (OU) habituellement utilisés en langage C
    $ [[ -e confidentiel && ! -r confidentiel ]]; echo $?
    1
  • les expressions arithmétiques sont évaluées à l’intérieur des ... .
$ a=2
$ b=4
$ [[ $a*3 -gt 10 ]]; echo $?
1
$ [[ $a*3 -eq $b+2 ]]; echo $?
0
  • lorsqu’on utilise l’opérateur == ou l’opérateur != la chaîne de caractères à droite de l’opérateur est comprise comme un motif. Dans ce cas ce n’est pas l’égalité ou la différence que l’on teste mais le filtrage par motif (pattern matching).

Dans un motif le caractère spécial * remplace n’importe quelle séquence de caractères (éventuellement aucun) ; le caractère spécial ? remplace n’importe quel caractère exactement une fois et les crochets permettent de remplacer exactement un des caractères indiqués entre les crochets.

$ [[ titu == t?t? ]]; echo $?
0
$ [[ taratata == t*a ]]; echo $?
0
$ [[ titu == t[aeiou]t[aeiou] ]]; echo $?
0

Challenge : X et Y

Ce que j'ai fait et qui fonctionne:

#!/bin/bash
 
if [ $# -ne 0 ] 
then
    if [[ $1 == *X* && $1 == *Y* ]]
    then
        exit 0
    else
        exit 1
    fi
else
  exit 1
fi

Ce qu'il fallais faire ! re LOL

### fichier exo3.sh ###
#!/bin/bash
[[ "$1" == *X*Y* || "$1" == *Y*X* ]]

tests numériques avec la commande (( ... ))

Nous avons vu dans l’activité 3.4 que la commande ( ( … ) ) permet de réaliser des calculs d’expressions arithmétiques et en particulier des tests arithmétiques. Cette commande n’affiche rien mais son code retour peut être utilisé pour réaliser des tests.
Dans le cas de tests arithmétiques si l’expression entre les doubles parenthèses est vraie le code retour vaut 0 et il vaut 1 sinon ce qui est équivalent aux résultats obtenus avec la commande test

$ a=2
$ b=4
$ [ $a -lt $b ]; echo $?
0
$ (( a < b )); echo $?
0
$ [ $a -eq $b ]; echo $?
1
$ (( a == b )); echo $?
1

Attention cependant si l’expression arithmétique n’est pas un test car l’interprétation du code retour est délicate. En effet le code retour obtenu est 0 si la valeur de l’expression entre les doubles parenthèses est différente de 0 et 1 si la valeur de l’expression vaut 0.
Ainsi le résultat d’une expression arithmétique dont on utilise le code retour comme un test est équivalent à faux si la valeur de l’expression est 0 et vrai sinon. Il est donc important de ne pas confondre le code retour et la valeur de l’expression entre parenthèse.

$ a=4
$ (( a-4 )); echo $?
1
$ (( a-2 )); echo $?
0
$ (( a / 10 )); echo $?
1
$ (( a / 2 )); echo $?
0

La valeur de l’expression arithmétique a − 4 est 0 donc le code retour de ( ( a-4 ) ) est 1, ce qui, utilisé comme un test, est équivalent à faux. L’interprétation serait donc « a est-il différent de 4 ? ». De la même manière l’interprétation du code retour de ( ( a-2 ) ) serait « a est-il différent de 2 ? » ; et pour ( ( a/10 ) ) : « la division entière de a par 10 est-elle différente de 0 ? » ou autrement dit « a est-il supérieur ou égal à 10 ? ».

Challenge 42: ordre alphanumérique croissant

Ce que j'ai fait et qui fonctionne:

#!/bin/bash
 
if [ $# -eq 3 ] 
then
    # args list control:
    if [[ $1 < $2 && $2 < $3 ]]
    #if [[ $1 < $2 ]]
    then
        exit 0
    else
        exit 1
    fi
else
    exit 1
fi

Ce qu'il fallait faire lol

### fichier exo1.sh ###
 #!/bin/bash
[ $# -eq 3 -a "$1" \‹ "$2" -a "$2" \‹"$3"	]

Conditionner l'exécution de commandes

Conditionner l'exécution d'une suite de commandes

enchaînementcode retour
de cmd1
code retour
de cmd1
cmd1 ; cmd2 quelconque toujours
cmd1 && cmd2 0 ok
!=0 nok
oui
non
cmd1 dbl pipe cmd2 0 ok
!=0 nok
non
oui
# SI le fichier existe, on l'affiche:
$ [ -f monCV ] && cat monCV
$ echo $?
1    # le fichier n'existe pas
# SI le fichier existe, on l'affiche SINON on affiche un message
$ [ -f monCV ] && cat monCV || echo "le fichier n'existe pas"
le fichier n'existe pas

Challenge 43Q1: oui non et code retour

#!/bin/bash
 
[ $# -eq 0 ] && echo "Vous n'avez pas donne votre reponse" && exit 3; 
[ $# -gt 1 ] && echo "Donnez une seule reponse" && exit 4;
[ $# -eq 1 ] && [ $1 = "o" -o $1 = "O" ] && echo "oui" && exit 0;
[ $# -eq 1 ] && [ $1 = "n" -o $1 = "N" ] && echo "non" && exit 1;
echo "Pas compris" && exit 5

Correction:

### ouinon.sh  ###
#!/bin/bash
[ $# -eq 0 ] && echo "Vous n'avez pas donné votre réponse" && exit 3
[ $# -gt 1 ] && echo "Donnez une seule réponse" && exit 4
[ "$1" = o ] || [ "$1" = O ] && echo oui && exit 0
[ "$1" = n ] || [ "$1" = N ] && echo non && exit 1
echo "Pas compris" && exit 5

if: Contrôle conditionnel dans un script shell

#!§bin/bash
 
if [ -f message -a -w message ]
then
    echo "I love Bash !" >> message
elif [ ! -e message ]
then
    echo "I love Bash !" > message
elif [ -f message -a ! -w message]
then
    chmod u+w message
    echo "I love Bash !" >> message
fi

Challenge 43Q2

#!/bin/bash
 
if [[ $# > 1 && $# < 4 ]]
then
    # 1er arg
    if [ -r $1 -a -r $1 -a -w $1 ]
    then
        # 2ème arg
        if [ -d $2 -a -x $2 ]
        then 
            # 3ème arg
            if [ $# -eq 3 ]
            then
                if [ $3 -gt 0 ]
                then 
                    echo $3
                    exit 0
                else
                    echo "usage : testargu.sh fichier repertoire [nombre_positif ]"
                    exit 6
                fi
            # que 2 args
            else
                echo 999
                exit 0
            fi
        else
            echo "usage : testargu.sh fichier repertoire [nombre_positif ]"
            exit 5
        fi
    else
        echo "usage : testargu.sh fichier repertoire [nombre_positif ]"
        exit 4
    fi
else
    echo "usage : testargu.sh fichier repertoire [nombre_positif ]"
    exit 3
fi

Correction :

### testargu.sh  ###
#!/bin/bash
usage="usage : $0 fichier répertoire  	[nombre_positif]"
# vérification du nombre d'arguments
if [ $# -ne 2 -a $# -ne 3 ]
then
    echo $usage&2
    exit 3
fi
# on teste le premier argument
if [ ! -f $1 ] || [ ! -r $1 ] || [ ! -w $1 ]
then
    echo $usage&2; exit 4
fi
# on teste le deuxième argument
if [ ! -d $2 ] || [ ! -x $2 ]
then
    echo $usage&2; exit 5
fi
# s'il y en a un, on teste le 3e argument
if [ $# -eq 3 ]
then
    if [ $3 -gt 0 ]
    then
        echo $3; exit 0
    else
        echo $usage&2; exit 6
    fi
fi
# Arriver ici il y a exactement 2 arguments
echo 999

case

La syntaxe de l’instruction case est la suivante :

case expression in
motif1)
    lignes-commandes1;;
motif2)
    lignes-commandes2;;
...
motifN)
    lignes-commandesN;;
esac

Le script suivant écrit le type d’un fichier donné en argument en se fondant sur l’extension du nom de fichier. On utilise le caractère spécial * dans les filtres pour ignorer le nom du fichier jusqu’à l’extension. Le troisième cas est constitué de trois filtres séparés par une barre verticale.

#!/bin/bash
# on teste d’abord qu’il y a bien exactement un argument
# si ça n’est pas le cas on écrit un message d’erreur
# qui rappel l’usage du script
if [ $# -ne 1 ]
then
    echo "Erreur : nombre d’argument incorrect" > &2
    echo "Usage : $0 fich" > &2
else
    case $1 in
    *.c)
        echo "Code source en langage c";;
    *.sh)
        echo "Script bash";;
    *.jpeg | *.jpg | *.png)
        echo "Image";;
    *)
        echo "Type de fichier non reconnu";;
    esac
fi

Ce deuxième exemple demande de répondre par oui ou non à une question donnée en argument (à condition qu’il y en ait une). Il prévoit toutes les formes de réponses possibles pour oui ou non.

#!/bin/bash
if [ $# -eq 1 ]
then
    echo "$1 ?"
    echo "Etes vous d’accord (oui ou non) ?"
    read rep
    case $rep in
    [oO] | [oO][uU][iI])
        echo "Ok, merci";;
    [nN] | [nN][oO][nN])
        echo "Tres bien, je respecte votre choix";;
    *)
        echo "Desole, je ne comprends pas votre reponse";;
    esac
fi

Les boucles

while

repeat.sh

#!/bin/bash
 
MOT=${1:?"Vous devez indiquer un mot"}
NB=${2:-10}
 
i=0
while [ $i - lt $NB ] ; do
    echo "$MOT" 
    i=$(( $i + 1 ))
done
$chmod +x repeat.sh
$ ./repeat.sh Bonjour 3
Bonjour
Bonjour
Bonjour

for

testFor.sh

#!/bin/bash
 
for CUR in Pierre Paul Jacques ; do
    echo $CUR
done
$ chmod +x testFor.sh
$ ./testFor.sh
Pierre
Paul
Jaques
$

afficheArg.sh

#!/bin/bash
 
NUM=1
 
for V in $@ ; do
    echo "argument $NUM = $V"
    NUM=$(( $NUM + 1 ))
done
$ chmod +x afficheArg.sh
$ ./afficheArg.sh
$ ./afficheArg.sh yop yep yap
argument 1 = yop
argument 2 = yep
argument 3 = yap

seq

Génère une suite de nombre entre 2 valeurs:

$ seq 1 5
1 2 3 4 5

repeatFor.sh

#!/bin/bash
 
MOT=${1:?"Vous devez indiquer un mot"}
NB=${2:-10}
 
for i in $( seq 1 $NB ] ; do
    echo "$MOT" 
done
$chmod +x repeatFor.sh
$ ./repeatFor.sh Bonjour 3
Bonjour
Bonjour
Bonjour

Lecture de fichier avec while et read

read.sh

#!/bin/bash
while read ligne; do
    echo -n "Ma ligne : "
    echo $ligne
done < $1
$ ./read.sh read.sh
Ma ligne : #!/bin/bash
Ma ligne :
Ma ligne : while read ll; do
Ma ligne : echo -n "Ma ligne : "
Ma ligne : echo $ll
Ma ligne : done < $1

La commande continue

La commande continue saute directement à l’itération / répétition suivante dans la boucle en n’effectuant pas les autres commandes restantes dans le cycle actuel de la boucle. Prenons tout de suite un exemple que nous allons commenter ensuite. Soit le script suivant que l’on appellera continue.sh

#!/bin/bash
a=0
while [ "$a" -le 10 ]; do
    a=$(($a+1))
    if [ "$a" -eq 2 ] || [ "$a" -eq 8 ]
    then
        continue   # va directement à l’itération suivante de la boucle while
    fi
    echo -n "$a "  # Cette partie ne sera pas exécutée pour a=2 et a=8
done
$ ./continue.sh
1 3 4 5 6 7 9 10

Nous voyons dans le résultat que ni 2 ni 8 ne sont affichés.

break

break.sh

#!/bin/bash
a=0
while [ "$a" -le 10 ]; do
    a=$(($a+1))
    if [ "$a" -eq 2 ] || [ "$a" -eq 8 ]
    then
        break  # quitte directement la boucle while
    fi
    echo -n "$a " # Cette partie ne sera pas exécutée pour a supérieur ou égal à 2
done
$ ./break.sh
1

Nous voyons dans le résultat que la boucle s’arrête dès que la valeur de a est supérieure ou égale à 2. La commande break renvoi directement à la ligne done. La commande break peut aussi prendre en paramètre un niveau d’imbrication de boucle (paramètre numérique). Par exemple, break 3 va “terminer” trois boucles imbriquées.

Autre structure de la boucle for

La boucle for peut aussi prendre une syntaxe héritée du langage de programmation C. Cette variante est caractérisée par trois paramètres qui contrôlent la boucle for. Ces trois paramètres sont :

  • EXP1 : qui est l’initialisation de la boucle,
  • EXP2 : qui présente le test qui conditionne l’exécution du prochain tour de boucle, et
  • EXP3 : qui spécifie l’expression de comptage.
for (( EXP1; EXP2; EXP3 ))
do
    command1
    command2
    command3
done

for3.sh

#!/bin/bash
for ((i=1; i<=4; i++)); do
    echo "Nombre $i"
done
$ ./for3.sh
Nombre 1
Nombre 2
Nombre 3
Nombre 4

TP: renommer fichier avec tag _alice

Ce que j'ai fait:
Jj'ai géré les nb de point d'ext comme: projet1.tar.gz → projet1_alice.tar.gz

#!/bin/bash
 
nom_fichier=${1:?"Erreur : vous devez indiquer le nom du fichier a renommer"}
nb_delimits=$(echo $nom_fichier | grep -o "\." | wc -l)
nb_fields=$(expr $nb_delimits + 1)
for i in $( seq 1 $nb_fields ) ; do
    if [ $i -eq 1 ]
    then
        new=$(echo $nom_fichier | cut -d . -f${i})
        new=${new}_alice
    else
        new=$new.$(echo $nom_fichier | cut -d . -f${i})
    fi
done
mv $nom_fichier $new

Ce qu'il fallais faire LOL !

#!/bin/bash
# fichier : tagalice.sh
FULL_NAME=${1:?"Erreur : vous devez indiquer le nom du fichier à renommer"}
#On va isoler le nom du fichier (partie avant le premier point)
NAME=$(echo $FULL_NAME | cut -d. -f1)
 
#On va isoler l'extension du fichier (partie apres le premier point)
#NB: on considere qu'il y a toujours au moins un point dans le nom
EXT=$(echo $FULL_NAME | cut -d. -f2-)
 
#Construction du nouveau nom
NEW_NAME="${NAME}_alice.$EXT"
 
#On renomme le fichier
mv "$FULL_NAME" "$NEW_NAME"
########################

Les fonctions

Il faut les déclarer en début de script

nom_fonction() {
    commande1
    commande2
    ...
}

Exemple : hello.sh

#!/bin/bash
say_hello() {
echo "Hello World !!!"
}
say_hello
say_hello
$ chmod +x hello.sh
$ ./hello.sh
Hello World !!!
Hello World !!!
$

Les paramètres d’une fonction

On utilise les même variables :

  • $1, $2, $3, … qui vont contenir les valeurs des paramètres de la fonction.
  • $# qui aura pour valeur le nombre de paramètres transmis au moment de l’appel.
  • $@ et $* qui contiendront l’ensemble des paramètres, le premier dans une liste et le second dans un chaîne de caractères.

Exemple : modif de hello.shhello.sh

#!/bin/bash
#définition de la fonction
say_hello() {
    echo "Hello $1 !!!"
}
#utilisation de la fonction
say_hello Me
say_hello You

Les variables locales à une fonction

Quand on manipule une variable dans un script, les changements subis par cette variable valent pour l’ensemble du script. Et c’est aussi le cas pour les variables utilisées dans le code d’une fonction. Aussi, on dit que les variables sont globales au script, ou encore que la portée d’une variable est globale.
Il est cependant possible de limiter la portée d’une variable dont l’usage ne concerne que l’espace du corps d’une fonction. Cela permet d’éviter d’entrer en conflit avec les autres traitements du script.
Pour obtenir cette limitation il suffit d’utiliser la commande local. Cette commande doit se placer au début du code de la fonction, et elle prend en paramètre le nom de la variable concernée avec éventuellement la valeur de départ de celle-ci. :

    local nom_variable
ou 
    local nom_variable=valeur

Retours des fonctions

#!/bin/bash
max() {
    if [ $1 -gt $2 ]; then
        return $1
    else
        return $2
    fi
}
IS_OK=${2:?"Vous devez fournir 2 valeurs"}
max $1 $2
echo $?
Attention :
return est limité de 0 à 255

Il existe 2 stratégies de contournement :

  • La première consiste à utiliser une variable globale au script. A la fin de son traitement la fonction devra fixer la valeur produite dans cette variable. Le code qui a invoqué la fonction n’aura alors qu’à consulter la valeur de cette variable juste après la commande d’appel.
  • La seconde stratégie consiste à envoyer le résultat de la fonction sur la sortie standard, avec la commande echo. On récupère ensuite cette valeur en utilisant la syntaxe de substitution de commande : $( … ).

Amélioration du script afficheMax.sh

#!/bin/bash
max() {
    if [ $1 -gt $2 ]; then
        echo $1
    else
        echo $2
    fi
}
 
IS_OK=${2:?"Vous devez fournir 2 valeurs"}
REP=$(max $1 $2)
echo $REP

Les alias

$ alias
alias ll=’ls -al’

La définition d’un alias suit la syntaxe suivante :

alias nomAlias="commande à exécuter"
Les alias n'ont pas de paramètre

La commande unalias supprime un alias existant.

$ unalias ll

Les fonctions en mode interactif

Exemple d’utilisation d’une fonction que nous définissons dans le fichier .bashrc.

mkd() {
mkdir -p "$@" && cd "$@"
}
$ mkd /tmp/t/t/t/t/t/t/t/t/t
$ pwd
/tmp/t/t/t/t/t/t/t/t/t

Une bonne pratique est de séparer les fichiers contenant les alias et les fonctions en créant des fichiers .alias et .func et de rajouter dans le fichier .bashrc les lignes suivantes :

$ cat .barhrc
[...]
source .alias
source .func
[...]