Logo Khaganat
Traductions de cette page?:

Cette page n'est pas tagué correctement.

Ceci est une ancienne révision du document !


Bonnes pratiques

Cette page liste dans une première partie quelques bonnes pratiques. Dans une seconde partie il sera question d'une sorte de trucs & astuces, des options méconnues de certaines commandes plus ou moins connues, ainsi que des commandes pas toujours très connues. Enfin en troisième partie, on revient un petit peu aux bonnes pratiques, aux choses auxquelles faire attention lorsque l'on administre un système.

Libre à vous d'étoffer l'article, toute contribution est bienvenue !

Bash / Shellscript

Shebang!

La première ligne d'un script shell contient toujours ce qu'on appelle le shebang, qui est utile pour indiquer au système qu'il s'agit d'un script shell (ou php, ou python, ou perl, etc). Dans le cas de bash, le shebang se présente sous cette forme :

#!/bin/bash

Parfois on voit aussi des scripts shell commencer par #!/bin/sh. La bonne pratique veut que l'on n'utilise cette forme (avec /bin/sh et non /bin/bash) que pour les scripts d'init, car le shell /bin/sh n'est pas nécessairement bash. Dans le cas de Debian notamment, il s'agit de dash, un shell allégé spécifiquement pour lancer ces scripts d'initialisation. Si vous comptez utiliser des spécificités bash, donc, préférez #!/bin/bash.

Sous-commandes (subcommands)

Il existe en shellscript deux possibilités pour exécuter des sous-commandes :

`avec des backticks`

ou alors

$(avec $parenthèses)

La première solution ne devrait être utilisée que dans les cas où cela s'avère absolument nécessaire (un script écrit pour de très vieux shells Bourne non-POSIX). Dans tous les cas il faut préférer la forme $().

Référence : http://mywiki.wooledge.org/BashFAQ/082

Largeur

Pour des raisons historiques, la convention veut que les lignes d'un script ne dépassent pas les 88 caractères. Si toutefois vous deviez écrire des lignes plus longues que ça, vous pouvez les étaler sur plusieurs lignes en mettant \ (barre oblique inversée + espace) avant chaque retour à la ligne.

Indentation

Attention, troll incoming ! Il n'y a pas de convention à ce jour. Généralement, on voit trois variantes (si vous en connaissez d'autres, n'hésitez pas) :

  • 2 espaces
  • 4 espaces
  • 1 tabulation

Noms des variables

Encore un troll… On peut écrire le nom des variables en $MAJUSCULE ou en $minuscule (ou en $mIXeD, mais c'est plus moche qu'autre chose, ça rend le tout illisible – autant écrire du brainfuck). Une convention veut que les noms des variables soient écrits en minuscules, avec un préfixe pour éviter la collision avec des noms réservés (comme par exemple $PATH, cf. la documentation de bash pour une liste complète). Cela pourrait ressembler à ça : $my_email, $my_source_path, etc.

Fonctions

Déclarer une fonction sans utiliser le mot-clef function, c'est obsolète.

Toujours utiliser des guillemets

Parfois un exemple vaut mille mots :

my_sentence="Cachez ces bits             que je ne saurais voir !"
echo $my_sentence
Cachez ces bits que je ne saurais voir !
echo "$my_sentence"
Cachez ces bits             que je ne saurais voir !

eval

Ne jamais utiliser eval, à moins de savoir ce que l'on fait ! eval fait que du code est exécuté deux fois. Si par exemple une variable contient du code, eh bien il sera exécuté. Cela est potentiellement une faille de sécurité béante, si on ne fait pas attention à ce qu'on fait.

Plus de détails ici https://mywiki.wooledge.org/BashFAQ/048

Fail early

C'est une expression à prendre au pied de la lettre : si une erreur doit avoir lieu, cela doit se passer le plus tôt possible. Par conséquent, une bonne pratique est, avant même d'écrire le code que l'on veut que notre script exécute, de vérifier si toutes les commandes qui y seront utilisées sont bien existantes :

my_needed_commands="sed awk lsof who"
missing_counter=0
for needed_command in $my_needed_commands; do
  if ! hash "$needed_command" >/dev/null 2>&1; then
    printf "Command not found in PATH: %s\n" "$needed_command" >&2
    ((missing_counter++))
  fi
done

if ((missing_counter > 0)); then
  printf "At least %d commands are missing in PATH, aborting\n" "$missing_counter" >&2
  exit 1
fi

On pourrait même pousser l'idée plus loin en testant les options des commandes utilisées (je pense par exemple à awk, pour qui il existe plein d'implémentation différentes).

Code de sortie

Toujours sortir du script avec un bon code de sortie (“exit status”) et éviter à tout prix de toujours terminer avec un code 0. Si ce n'est pas fait comme il faut, le script peut potentiellement produire des erreurs et continuer tout de même. Cela devient encore pire si le script est utilisé dans une suite de plusieurs commandes.

Liens

http://mywiki.wooledge.org/BashGuide/Practices (la bible bash, à avoir absolument sur votre table de chevet !) http://wiki.bash-hackers.org/scripting/style

Commandes unix

sar : System Activity Reporter

sar -A

C'est une sorte de monitoring de la machine locale.

ethtool

ethtool -p eth0 # same as "ethtool --identify eth0"
ethtool -t eth0 # selftest
ethtool -S eth0 # same as "ethtool --statistics eth0"

ethtool permet d'avoir des infos sur le matériel réseau, mais aussi d'identifier une interface physique en en faisant clignoter les LEDs (option -p).

nc / netcat

netcat permet à la base d'établir une connexion TCP, un peu comme le permet la commande telnet, à la différence que netcat procède uniquement à la connexion TCP, tandis que telnet tente d'établir une connexion au service de comande à distance éponyme (il y a toute la communication du protocole telnet en plus).

netcat permet aussi par exemple de transférer des fichiers d'un système à un autre :

$ nc -l 10.1.1.149 1234 > filename.out # à exécuter côté serveur
$ nc 10.1.1.149 1234 < filename.in # à exécuter côté client

On peut aussi scanner des ports avec netcat, pour voir si un port est ouvert ou fermé :

$ nc -zv 10.1.1.149 20-30
$ nc -zv 10.1.1.149 22 80 443

La première commande scanne les ports 20 à 30, la seconde scanne les ports 22, 80 et 443 de l'adresse IP 10.1.1.149.

at

at permet de programmer une commande à exécuter une seule fois, contrairement à cron qui est plutôt destiné à des tâches récurrentes.

echo "tar czf /tmp/homes.tgz /home" | at 1337

Créer un tarball compressé avec gzip du dossier /home dans le fichier /tmp/homes.tgz à 13:37.

cal

Calendrier :

$ cal
     March 2018       
Su Mo Tu We Th Fr Sa  
             1  2  3  
 4  5  6  7  8  9 10  
11 12 13 14 15 16 17  
18 19 20 21 22 23 24  
25 26 27 28 29 30 31

$ cal jan 1337
    January 1337      
Su Mo Tu We Th Fr Sa  
          1  2  3  4  
 5  6  7  8  9 10 11  
12 13 14 15 16 17 18  
19 20 21 22 23 24 25  
26 27 28 29 30 31

$ cal dec 9999
   December 9999      
Su Mo Tu We Th Fr Sa  
          1  2  3  4  
 5  6  7  8  9 10 11  
12 13 14 15 16 17 18  
19 20 21 22 23 24 25  
26 27 28 29 30 31

tr

tr permet la substitution de caractères dans une chaîne de caractères, tout en suivant une certaine logique. Quelques exemples :

$ echo "123 456 789" | tr " " "\n"
123
456
789

Remplacer toutes les espaces par un retour à la ligne.

echo "abcd" | tr "a-d" "p-s"
pqrs

Remplacer les caractères de 'a' à 'd' par 'p' à 's'.

echo "abcd" | tr "a-d" "1-4"

Remplacer les caractères de 'a' à 'd' par 1 à 4.

echo "abcd" | tr "abd" "678"
67c8

Remplacer les caractères 'a', 'b' et 'd' par 6, 7 et 8.

echo "abcd" | tr "ac" "xvpb"
xbvd

Remplacer les caractères 'a' et 'c' par 'x' et 'v'. N'y ayant pas d'autres caractère à “matcher” (le premier paramètre à la commande tr), 'p' et 'b' n'ont ici aucun intérêt et peuvent être omis / n'ont aucune incidence.

awk

awk est plus qu'une “simple” commande, c'est un langage de programmation permettant notamment de manipuler des chaînes de caractères. Voici quelques exemples, qui sont loin d'être exhaustifs quant à ce qu'il est possible de faire avec awk. Cela dit, ils donneront déjà un petit aperçu.

$ sentence="year-month-day hours:minutes:seconds :ms \"this is a comment\""
$ printf "%s\n" "$sentence" | awk '{print $1}'

Cela affiche le premier élément de la phrase, les éléments étant (par défaut) les chaînes de caractères séparées par une espace.

$ printf "%s\n" "$sentence" | awk '{print $NF}'
comment"

$NF est une variable spéciale pointant vers le dernier élément.

$ printf "%s\n" "$sentence" | awk 'BEGIN{FS = "\""} {print $(NF-1)}'
this is a comment

Ici on redéfinit le séparateur, qui est par défaut l'espace, pour utiliser à la place le caractère . On dit à awk d'afficher l'avant-dernier ($(NF-1)) élément.

$ printf "%s\n" "$sentence" | awk 'BEGIN{FS = " :"} {print $2}'
ms "this is a comment"

Redéfinition du séparateur à : (espace + double-point), et affichage du second élément.

$ printf "%s\n" "$sentence" | awk '/minutes/ {print $0}'
year-month-day hours:minutes:seconds :ms "this is a comment"

On peut aussi matcher des expressions rationnelles avec awk. Ici, toutes les lignes contenant le mot minutes seront affichées, $0 représentant la ligne entière.

$ printf "%s\n" "$sentence" | awk '/minutes/ {print $3}'
:ms

On peut aussi n'afficher que le troisième élément des lignes contenant le mot minutes.

$ lines="$(echo "line1 line2 line3" | tr " " "\n")"
$ printf "%s\n" "$lines" | awk '$1 ~ /line2/ {print $0}'
line2

On peut dire à awk de ne chercher une chaîne correspondante que dans un seul élément ; ici on cherche line2 dans le premier élément de chaque ligne.

$ printf "%s\n" "$lines" | awk '/line2/ {print $0}'
line2

Cela revient au final à la même chose que ci-dessus, étant donné que l'on n'a qu'un seul élément par ligne.

$ printf "%s\n" "$lines" | awk '/e3/ {print $0}'
line3

Eh oui, seul e3 est matché.

Options peu utilisées/connues

find

Pour supprimer des fichiers de manière automatisée on pourrait être tenté d'exécuter ce genre de commande :

rm -rf `find /var/tmp/ -type d -name .svn`

Cette commande cherche tous les dossiers nommés .svn dans /var/tmp/ de manière récursive, puis passe le résultat à la commande rm -rf. Le problème qui peut se poser est si dans cette liste de dossiers, l'un d'eux contient une espace dans son chemin, la commande va échouer et donc ce dossier ne sera pas supprimé.

Pour bien faire les choses, autant dire directement à la commande find de supprimer les dossiers trouvés, via l'option -exec :

find /var/tmp/ -type d -name .svn -exec rm -rf {} \;

grep / wc

On voit très très souvent des oneliners qui, pour compter le nombre de lignes font ceci :

[commande] | wc -l

Jusque là aucun souci. En revanche, quand on veut filtrer, très souvent on utilise grep :

[commande] | grep "machin" | wc -l

Là, ça commence à piquer les yeux. OK, vous allez dire que je suis un vieux de la vieille qui voit le mal partout, qui cherche à économiser le moindre octet en mémoire, qu'on n'est plus à une époque où la mémoire était chère et rare… Qu'à cela ne tienne ! Plus c'est court, plus c'est élégant, mieux c'est !

Ça commence à piquer les yeux parce-que grep sait très bien compter tout seul !

[commande] | grep -c "machin"

Et voilà ! L'option -c de grep retourne le nombre de lignes où machin a été trouvé.

Sysadmin en général

Reboot

Quoi que l'on fasse comme modification sur un système, il faut toujours veiller à ce qu'après un redémarrage la machine se retrouve dans un état identique à celui dans lequel elle était avant ce redémarrage.

Par exemple, quand on ajoute une route sur la CLI, bien veiller à ce que la route soit aussi configurée “en dur”. Dans le cas de Debian, on peut mettre la commande dans par exemple /etc/network/interfaces en ajoutant up devant la commande qu'on a exécuté.

CLI :
route add -host ftp.fr.debian.org gw 192.168.100.253
/etc/network/interfaces :
iface eth0 inet static
    address [...]
up route add -host ftp.fr.debian.org gw 192.168.100.253
down route del -host ftp.fr.debian.org gw 192.168.100.253

Attention à la seconde ligne avec down, ce n'est pas route add mais route del, pour supprimer la route lors de l'extinction ou du redémarrage.

Cela n'est qu'un exemple. À chaque modification il faut se poser la question : “Est-ce que ma modification est «reboot-proof» ?”

CC Attribution-Share Alike 4.0 International Driven by DokuWiki
fr/bestpractices.1522249616.txt.gz · Dernière modification : 2021/12/03 19:18 (modification externe)

Licences Mentions légales Accueil du site Contact