28. Des Zéros et des Nulls

/dev/zero ... /dev/null

Utilisation de /dev/null

Vous pouvez considérer /dev/null comme un trou noir. L'équivalent le plus proche serait un fichier en écriture seulement. Tout ce qui y est écrit disparaît à jamais. Toute tentative de lecture n'aboutira à rien. Néanmoins, /dev/null peut être très utile à la fois en ligne de commande et dans certains scripts.

Supprimer stdout.

cat $filename >/dev/null
# Le contenu de ce fichier ne s'affichera pas sur la sortie stdout.

Supprimer stderr (provenant de l'Exemple 15.3, « Badname élimine dans le répertoire courant les fichiers dont le nom contient des caractères incorrects et des espaces blancs. »).

rm $mauvaisnom 2>/dev/null
#           Donc les messages d'erreurs [stderr] disparaissent.

Supprimer les sorties à la fois de stdout et stderr.

cat $nom_de_fichier 2>/dev/null >/dev/null
# Si "$nom_de_fichier" n'existe pas, aucun message d'erreur ne s'affichera.
#  Si "$nom_de_fichier" existe bien, le contenu du fichier ne s'affichera pas sur
#+ la sortie stdout.
# Du coup, aucun affichage ne résultera de la ligne précédente.
#
#  Ceci peut être utile dans certaines situations où le code de retour d'une
#+ commande a besoin d'être testée, mais que sa sortie n'est pas souhaitée.
#
# cat $filename &>/dev/null
#     fonctionne aussi d'après l'indication de Baris Cicek.

Supprimer le contenu d'un fichier, mais en conservant le fichier lui-même, avec ses droits (provenant de l'Exemple 2.1, « cleanup : Un script pour nettoyer les journaux de trace dans /var/log  » et l'Exemple 2.3, « cleanup : Une version améliorée et généralisée des scripts précédents ») :

cat /dev/null > /var/log/messages
#  : > /var/log/messages   a le même résultat mais ne lance pas un nouveau
processus.

cat /dev/null > /var/log/wtmp

Vider automatiquement le contenu d'un fichier de traces (spécialement intéressant pour s'occuper de ces fichiers dégoutants que sont les « cookies » envoyés par les sites Web commerciaux) :

Exemple 28.1. Cacher le cookie jar

# Navigateur Netscape obsolète.
# Les mêmes principes s'appliquent aux derniers navigateurs.

if [ -f ~/.netscape/cookies ]  # À supprimer s'il existe.
then
  rm -f ~/.netscape/cookies
fi

ln -s /dev/null ~/.netscape/cookies
# Maintenant, tous les cookies se trouvent envoyés dans un trou noir plutôt que
# d'être sauvé sur disque.

Utilisation de /dev/zero

Comme /dev/null, /dev/zero est un pseudo fichier périphérique mais il produit réellement un flux de caractères zéros (des zéros binaires, pas du type ASCII). Toute écriture dans /dev/zero disparait et il est plutôt difficile de lire les zéros à partir de là bien que ceci puisse se faire avec od ou un éditeur hexadécimal. L'utilisation principale de /dev/zero est de créer un fichier factice initialisé à une taille prédéterminée pour en faire un fichier de swap temporaire.

Exemple 28.2. Créer un fichier de swap en utilisant /dev/zero

#!/bin/bash
# Crée un fichier de swap.

#  Un fichier swap fournit un espace de stockage pour le cache,
#+ ce qui aide à accélérer certaines opérations des systèmes de fichiers.

ROOT_UID=0                  # Root a l'$UID 0.
E_MAUVAIS_UTILISATEUR=65    # Pas root ?

FICHIER=/swap
TAILLEBLOC=1024
BLOCSMINS=40
SUCCES=0

# Ce script doit être exécuté en tant que root.
if [ "$UID" -ne "$ROOT_UID" ]
then
  echo; echo "Vous devez être root pour exécuter ce script."; echo
  exit $E_MAUVAIS_UTILISATEUR
fi  
  

blocs=${1:-$BLOCSMINS}           #  Par défaut à 40 blocs, si rien n'est
                                 #+ spécifié sur la ligne de commande.
# Ceci est l'équivalent du bloc de commandes ci-dessous.
# ------------------------------------------------------
# if [ -n "$1" ]
# then
#   blocs=$1
# else
#   blocs=$BLOCSMINS
# fi
# ------------------------------------------------------


if [ "$blocs" -lt $BLOCSMINS ]
then
  blocs=$BLOCSMINS              # Doit être au moins long de 40 blocs.
fi  


######################################################################
echo "Création du fichier swap d'une taille de $blocs blocs (Ko)."
dd if=/dev/zero of=$FICHIER bs=$TAILLEBLOC count=$blocs  # Vide le fichier.
mkswap $FICHIER $blocs             # Indique son type : swap.
swapon $FICHIER                    # Active le fichier swap.
#  Notez que si au moins une de ces commandes échouent,
#+ cela posera de sérieux problèmes.
######################################################################

#  Exercice :
#  Ré-écrivez le bloc de code ci-dessus de façon à ce que,
#+ si une erreur se produit:
#    1) un message d'erreur est envoyé sur stderr,
#    2) tous les fichiers temporaires sont nettoyés,
#    3) le script sort immédiatement avec un code d'erreur approprié.

echo "Fichier swap créé et activé."

exit $SUCCES

Une autre application de /dev/zero est de « remplir de zéros » un fichier d'une taille spécifiée pour une raison particulière, telle que monter un système de fichiers sur un périphérique loopback (voir l'Exemple 16.8, « Création d'un système de fichiers dans un fichier ») ou telle que la suppression « sécurisée » d'un fichier (voir l'Exemple 15.59, « Effacer les fichiers de façon sûre »).

Exemple 28.3. Créer un disque ram

#!/bin/bash
# ramdisk.sh

#  Un disque ram ("ramdisk") est un segment de mémoire RAM système agissant
#+ comme un système de fichiers.
#  Son avantage est son accès très rapide (temps de lecture/écriture).
#  Inconvénients : volatile, perte de données au redémarrage ou à l'arrêt,
#                  moins de RAM disponible pour le système.
#
#  En quoi un disque ram est intéressant ?
#  Conserver un ensemble de données large, comme une table ou un dictionnaire,
#+ sur un disque ram, accélère les recherches de données car l'accès mémoire est
#+ bien plus rapide que l'accès disque.


E_UTILISATEUR_NON_ROOT=70      # Doit être root.
NOM_UTILISATEUR_ROOT=root

POINT_MONTAGE=/mnt/ramdisk
TAILLE=2000                    # 2000 blocs (modifiez comme vous l'entendez)
TAILLE_BLOC=1024               # 1K (1024 octets) en taille de bloc
PERIPH=/dev/ram0               # Premier périphérique ram

nom_utilisateur=`id -nu`
if [ "$nom_utilisateur" != "$NOM_UTILISATEUR_ROOT" ]
then
  echo "Vous devez être root pour exécuter \"`basename $0`\"."
  exit $E_UTILISATEUR_NON_ROOT
fi

if [ ! -d "$POINT_MONTAGE" ]   #  Teste si le point de montage est déjà créé,
then                           #+ pour qu'il n'y ait pas d'erreur après
  mkdir $POINT_MONTAGE         #+ plusieurs exécutions de ce script
fi

##############################################################################
dd if=/dev/zero of=$PERIPH count=$TAILLE bs=$TAILLE_BLOC  # Vide le périphérique
                                                          #+ ram
mke2fs $PERIPH                 # Crée un système de fichiers ext2 dessus.
mount $PERIPH $POINT_MONTAGE   # Monte le périphérique.
chmod 777 $POINT_MONTAGE       #  Pour que les utilisateurs standards puissent y
                               #+ accéder.
                               # Néanmoins, seul root pourra le démonter.
##############################################################################
#  Nécessite de tester si les commandes ci-dessus ont réussi. Pourrait poser
#+ problème sinon.
#  Exercice : modifiez ce script pour le rendre plus sûr.

echo "\"$POINT_MONTAGE\" est maintenant disponible"
# Le disque ram est maintenant accessible pour stocker des fichiers, y compris
# par un utilisateur standard.

#  Attention, le disque ram est volatile et son contenu disparaîtra au prochain
#+ redémarrage ou au prochain arrêt.
#  Copiez tout ce que vous voulez sauvegarder dans un répertoire standard.

# Après redémarrage, lancez de nouveau ce script pour initialiser le disque ram.
# Remonter /mnt/ramdisk sans les autres étapes ne fonctionnera pas.

#  Correctement modifié, ce script peut être appelé dans /etc/rc.d/rc.local,
#+ pour initialiser le ramdisk automatiquement au lancement.
#  Cela pourrait être approprié, par exemple, pour un serveur de bases de données.

exit 0

En plus de tout ce qui se trouve ci-dessus, /dev/zero est nécessaire pour les binaires UNIX/Linux ELF (acronyme de Executable and Linking Format).