Quelques scripts pour bien utiliser Linux
Dans Linux, un script est un fichier rassemblant une succession de commandes.
Toutes les commandes peuvent être écrites les unes à la suite des autres.
Elles sont exécutées séquentiellement par le shell.
Pour une bonne exécution du script, il est toutefois conseillé de le structurer correctement pour éviter les mauvaises surprises.
Personnellement, je respecte toujours les règles suivantes :
1 - Toujours nommer son script avec l'extension ".sh" (facilite la recherche)
2 - La première ligne d'un script doit toujours indiquer quel interpréteur de commande utiliser, par exemple :
#!/bin/bash
#!/bin/sh
#!/usr/bin/python
#!/usr/bin/php
3 - Renseigner la variable PATH :
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
La variable PATH permet d'indiquer au shell les répertoires où sont situés les commandes externes utilisées.
En console, la variable PATH est initialisée à la connexion ce qui fait qu'il n'est pas nécessaire d'indiquer le chemin complet d'une commande afin de l'exécuter. Si le script est exécuté uniquement en console, il n'est pas nécessaire d'initialiser la variable PATH mais si le script est exécuté via une tâche cron, la variable PATH est inconnue, dans ce cas, toutes les commandes externes utilisées dans le script ne pourront pas être exécutées.
4 - Rediriger la sortie d'erreur standard dans un fichier de log spécifique, par exemple :
exec 2>>/var/log/mon_script_error.log
exec 2>/dev/null (s'il n'est pas nécessaire de "logguer" les différentes erreurs rencontrées)
5 - Terminer le script avec la commande suivante :
exit 0
Exemple avec un script basique :
$ cat mon_script.sh
#!/bin/bash
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
exec 2>/dev/null
...
mes commandes
...
exit 0
Pour effectuer une sauvegarde différentielle, il faut utiliser le programme rdiff-backup.
Pour l'installer :
$ sudo apt-get install rdiff-backup
Pour créer une sauvegarde différentielle :
Créer un fichier, par exemple sauvegarde_differentielle.sh et y inscrire les deux lignes suivantes
$ cat sauvegarde_differentielle.sh
#!/bin/sh
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
exec 1>>/var/log/sauvegarde_differentielle.log
exec 2>>/var/log/sauvegarde_differentielle_error.log
nice -n 19 rdiff-backup /repertoire_a_sauvegarder /destination_de_la_sauvegarde && nice -n 19 rdiff-backup --remove-older-than 1W --force /destination_de_la_sauvegarde
exit 0
$
Voir également cette rubrique pour plus de renseignements
Ecrire dans le fichier la ligne suivante
$ cat maj.sh
#!/bin/sh
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
sudo apt-get update && sudo apt-get upgrade
exit 0
Pour exécuter ce script, taper dans la console
$ $MAJ
Une autre méthode, plus simple, consiste à créer un alias dans le fichier ~/.bashrc.
$ echo "alias maj='sudo apt-get update && sudo apt-get upgrade'" >> ~/.bashrc
Prendre en compte le nouvel alias créé :
$ . ~/.bashrc
Pour l'exécution :
$ maj
Pour surveiller un processus et le redémarrer en cas de plantage, créer une tâche cron exécutée en tant que root avec la commande suivante :
Par exemple, pour le processus DOVECOT (serveur POP / IMAP)
$ pgrep dovecot > /dev/null || { /etc/init.d/dovecot restart ; }
$ cat sauvegarde.sh
#!/bin/bash
####### sauvegarde - Sauvegarde le sources.list et les paquets #######
echo
echo "Script de sauvegarde APT'"
echo
DATE=`date +%F`
if test -d $DATE
then echo "Le dossier \"$DATE\" existe déjà. Voulez-vous refaire la sauvegarde ? [o/n]"
read choix
if [ $choix = "n" ]
then
echo
echo "Abandon."
exit 0
elif [ $choix = "o" ]
then
echo
echo "Réécriture de la sauvegarde $DATE..."
else
echo "Veuillez taper \"o\" ou \"n\"."
exit 2
fi
else
mkdir $DATE
fi
( cp /etc/apt/sources.list ./$DATE/sources.$DATE.list && dpkg --get-selections > ./$DATE/paquets.$DATE.list ) \
&& echo "Sauvegarde terminée dans $PWD/$DATE" \
|| echo "Sauvegarde échouée..."
exit 0
$ cat restauration.sh
#!/bin/bash
####### restauration - Script de restauration APT #######
echo
echo "Script de restauration APT"
echo "Une connexion Internet fonctionnelle est requise pour le bon fonctionnement de ce script."
echo
##### DEBUT GESTION DES ERREURS #####
ls -1 | grep '^[[:digit:]]\{4\}\-[[:digit:]]\{2\}\-[[:digit:]]\{2\}$' > /dev/null
if test $? -ne 0
then
echo "Il n'y a rien à restaurer !"
exit 2
elif [ ! `whoami` = "root" ]
then
echo "Il faut être root ... Executez \"sudo !!\"."
exit 2
elif test $# -ne 1
then
echo "Il faut un seul paramètre. Tapez \"./restauration sources\" pour restaurer le sources.list ou \"./restauration paquets\" pour restaurer les paquets."
exit 2
fi
##### FIN GESTION DES ERREURS #####
##### DEBUT SELECTION D'UNE DATE #####
echo
echo "Par défaut le script restaure votre sauvegarde la plus récente. Voulez-vous en restaurer une autre ? [o/n]"
read choix
if [ $choix = "n" ] # Si il veut la plus récente, on prend la plus récente en fonction du nom des dossiers de sauvegarde
then
DATE=`ls -1 | grep '^[[:digit:]]\{4\}\-[[:digit:]]\{2\}\-[[:digit:]]\{2\}$' | tail -n 1`
echo "La date choisie est $DATE."
elif [ $choix = "o" ] # Sinon on lui demande de rentrer une date
then
echo "Veuillez entrer la date souhaitée, sous la forme AAAA-MM-JJ : "
read DATE
if test -d $DATE
then
echo "La date choisie est $DATE."
else
echo "La date demandée n'existe pas dans les sauvegardes !"
exit 2
fi
else
echo "Veuillez entrer \"o\" ou \"n\"."
exit 2
fi
echo
echo "Est-ce bien ce que vous voulez ? [o/n]" # Confirmation
read choix2
if [ $choix2 = "o" ]
then
echo "Continuons donc !"
sleep 2 # On dort 2 secondes pour la lisiblité
elif [ $choix2 = "n" ]
then
echo "Abandon."
exit 2
else
echo "Veuillez entrer \"o\" ou \"n\"."
exit 2
fi
echo
##### FIN SELECTION D'UNE DATE #####
##### DEBUT RESTAURATION #####
if test $1 = "sources" # Si on veut restaurer le sources.list
then
cp /etc/apt/sources.list /etc/apt/sources.list.svgorig.$DATE
( cp $DATE/sources.$DATE.list /etc/apt/sources.list && apt-get update ) \
&& (echo ; echo "Restauration du sources.list terminée avec succès.") \
|| (echo ; echo "Restauration du sources.list échouée... Vous n'avez probablement pas les droits d'écriture dans /etc/apt/" ; \
cp /etc/apt.sources.list.svgorig.$DATE /etc/apt/sources.list && echo "Votre sources.list original a été restauré." ; exit 2)
elif test $1 = "paquets" # Si on veut restaurer les paquets
then
( dpkg --set-selections < $DATE/paquets.$DATE.list && apt-get update ; apt-get dselect-upgrade ) \
&& (echo ; echo "Restauration des paquets terminée avec succès") \
|| (echo ; echo "Restauration des paquets échouée... Votre connexion Internet est peut-être défaillante." ; exit 2)
else
echo "Paramètre inconnu"
exit 2
fi
exit 0
##### FIN RESTAURATION #####
Détail du script
$ nl explode.sh
1 #!/bin/bash
2 chemin="chemin 1|chemin 2 chemin 2 et demi|chemin 3"
3 #Sauvegarde de la valeur du IFS
4 #La variable $IFS doit être obligatoirement entre guillemets
5 old="$IFS"
6 #Modification de la valeur du IFS avec le "|"
7 IFS="|"
8 #Creation du tableau
9 tab=( $chemin )
10 #Restauration de la valeur du IFS
11 #La variable $old doit être obligatoirement entre guillemets
12 IFS="$old"
13 #Parcourt du tableau
14 for (( i=0 ; i<${#tab[*]} ; i++ )) ; do
15 echo $i" -> "${tab[$i]}
16 done
17 exit 0
$
Résultat :
$ ./explode.sh
0 -> chemin 1
1 -> chemin 2 chemin 2 et demi
2 -> chemin 3
$
Le script suivant permet d'afficher, par caractères, le nombre de fois qu'il est utilisé.
$ cat compteCaracteres.awk
BEGIN {
RS = "\n"
}
{
split($0, tab, "")
for (var in tab) {
tab2[tab[var]]+=1
}
}
END {
for (var in tab2) {
printf "%1s --> %6d\n" , var , tab2[var]
}
exit 0
}
Exemple :
$ echo "ceci est un test" | awk -f compteCaracteres.awk
u --> 1
i --> 1
n --> 1
--> 3
c --> 2
e --> 3
s --> 2
t --> 3
On peut également effectuer un tri sur le résultat :
$ echo "ceci est un test" | awk -f compteCaracteres.awk | sort
--> 3
c --> 2
e --> 3
i --> 1
n --> 1
s --> 2
t --> 3
u --> 1
En utilisant un fichier en entrée :
$ cat file
TARGUANT
LOGOS
SOCRATISERONS
TEMPORISERENT
PLASTIQUAIENT
CORROBORER
BOTTELES
LIGOTA
SATINERONT
HYPNOTISERA
Affichage du résultat avec un tri décroissant sur le nombre de fois que le caractère est utilisé
$ cat file | awk -f compteCaracteres.awk | sort -b -n -k 3 -r
T --> 13
O --> 12
R --> 11
E --> 10
S --> 9
A --> 8
N --> 7
I --> 7
L --> 4
P --> 3
G --> 3
U --> 2
C --> 2
B --> 2
Y --> 1
Q --> 1
M --> 1
H --> 1
Voici un script qui permet d'afficher les adresses IP (v4) de toutes les interfaces réseau installées sur la machine.
Exécution :
$ ./getInetAddr.sh
eth0 ==> 10.33.43.10
lo ==> 127.0.0.1
wlan0 ==> 172.22.8.35
Ce script est disponible en téléchargement ici .
Egalement disponible via GIT :
$ git clone http://git.quennec.fr/ronan/scripts_pub.git
Détail du script commenté :
Voici un script qui permet de trouver les anagrammes d'un mot ou d'une suite de lettres.
Ce script utilise un dictionnaire de plus de 336500 mots.
Ce dictionnaire de mots peut être téléchargé ICI.
Il est également disponible ICI au format UTF-8.
Ce script s'utilise de cette manière :
$ anagramme.sh -f fichierDictionnaire [-d] -l nbLettre -c listeLettres
L'option -f permet d'indiquer le fichier "dictionnaire" à utiliser.
L'option -l permet d'indiquer le nombre de lettres des anagrammes à rechercher.
L'option -c permet d'indiquer le mot ou les lettres des anagrammes à rechercher.
L'option -d permet de ne pas prendre en compte les caractères accentués.
Cliquez ICI pour télécharger le script ou via mon GitLab.
Exécution du script :
# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -l 6 -c aspire
Liste des mots de 6 lettre(s) et contenant les lettres "aspire" :
1 - aspire
2 - paires
3 - paries
4 - parsie
5 - repais
# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -l 8 -c fuaaieujoslw
Liste des mots de 8 lettre(s) et contenant les lettres "fuaaieujoslw" :
1 - jalousai
2 - jalousie
Avec l'option -d :
# ./scripts/anagramme.sh -f liste.de.mots.francais.frgut.txt.utf8 -d -l 6 -c aspire
Liste des mots de 6 lettre(s) et contenant les lettres "aspire" :
1 - aspire
2 - aspire
3 - epairs
4 - paires
5 - paries
6 - paries
7 - parsie
8 - repais
Ci-dessous le détail du script avec un maximum de commentaires :
# nl scripts/anagramme.sh
1 #!/bin/bash
2 # Activation du debug
3 # Décommenter pour activer
4 #set -x
5 # Fonction permettant de tester l'existence des commandes passées en argument
6 function preRequis {
7 for arg in $@; do
8 if ! which $arg >/dev/null; then
9 logger -t $0 "La commande $arg n'est pas installée"
10 echo "La commande $arg n'est pas installée !!!"
11 echo "Fin du script."
12 exit 1
13 fi
14 done
15 }
16 # Journalisation de l'exécution du script
17 logger -t $0 "Exécution du script"
18 # Initialisation de la variable PATH
19 PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
20 # Exécution de la fonction preRequis
21 preRequis grep sed awk
22 # Vérification du nombre d'arguments passés au script
23 #if [[ ! $# -eq 6 ]]; then
24 if [[ $# -lt 6 || $# -gt 7 ]]; then
25 echo "Nombre d'arguments incorrect"
26 echo "Utilisation : $0 -f fichierDictionnaire [-d] -l nbLettre -c listeLettres"
27 exit 1
28 fi
29 # Suppression des caractères accentués (0 = NON / 1 = OUI)
30 supprAccents=0
31 # Validation des arguments passés au script
32 while getopts ":f:c:l:d" option; do
33 case $option in
34 f)
35 if [ -f "$OPTARG" ]; then
36 LISTE="$OPTARG"
37 else
38 echo "L'option -f requiert un fichier existant."
39 exit 1
40 fi
41 ;;
42 c)
43 listeDesLettres="$OPTARG"
44 if ! grep -E -q -i "^[a-z]*$" <<< "$listeDesLettres"; then
45 echo "L'option -c requiert les lettres [a-z]."
46 exit 1
47 fi
48 ;;
49 l)
50 nbLettre="$OPTARG"
51 if ! grep -E -q "^[0-9]*$" <<< "$nbLettre"; then
52 echo "L'option -l requiert une valeur numérique."
53 exit 1
54 fi
55 ;;
56 d)
57 supprAccents=1
58 ;;
59 :)
60 echo "L'option $OPTARG requiert un argument."
61 exit 1
62 ;;
63 \?)
64 echo "$OPTARG : option invalide."
65 exit 1
66 ;;
67 esac
68 done
69 # Initialisation des variables utilisées pour le script
70 listeDesLettres2="$listeDesLettres"
71 listeDesMots2=""
72 ind=1
73 # Réécriture de la liste des lettres en incluant un pipe entre chaque lettre pour l'utiliser avec la commande grep
74 # abcdef -> a|b|c|d|e|f|
75 listeDesLettres=$(sed -r 's/([a-zA-Z])/\1\|/g' <<< "$listeDesLettres")
76 # Recherche tous les mots contenant le nombre et les lettres indiqués
77 if [[ $supprAccents = "0" ]]; then
78 listeDesMots=$(cat "$LISTE" | grep -E -e "^.{$nbLettre}$" | grep -E -i "^[$listeDesLettres]+$")
79 else
80 listeDesMots=$(cat "$LISTE" | sed 'y/àâäéèêëîïôöùûüç/aaaeeeeiioouuuc/' | grep -E -e "^.{$nbLettre}$" | grep -E -i "^[$listeDesLettres]+$")
81 fi
82 # On parcourt tous les mots trouvés par la commande précédente.
83 # Chaque mot et la liste des lettres sont passés à la commande AWK.
84 # AWK initialise un tableau avec la liste des lettres (avec la commande split).
85 # Chaque lettre est remplacée dans le mot par un blanc avec la commande sub ...
86 # ... et le résultat renvoyé par la commande (1 en cas de succès et 0 en cas d'échec) ...
87 # ... est multiplié à la variable "a". Pour finir, la commande AWK retourne le résultat de la variable "a".
88 # Si la commande AWK retourne "1" (signifiant une erreur en BASH - D'où le "!" après le "if" )
89 # c'est que toutes les lettres du mot correspondent à la liste des lettres à chercher.
90 # On sauvegarde donc le mot en cours dans la variable "listeDesMots2".
91 while read mot; do
92 if ! echo "$mot|$listeDesLettres2" | awk -F'|' 'BEGIN{a=1;b=1}{split($1,tab,"");for(var in tab){b=sub(tab[var],"",$2);a*=b}}END{exit a}'; then
93 listeDesMots2="$listeDesMots2\n$ind - $mot"
94 ind=$(expr $ind + 1)
95 fi
96 done <<< "$listeDesMots"
97 # Affichage de la liste des mots trouvés
98 echo -n "Liste des mots de $nbLettre lettre(s) et contenant les lettres \"$listeDesLettres2\" :"
99 echo -e "$listeDesMots2"
100 # Journalisation de la fin du script
101 logger -t $0 "Fin d'exécution du script"
102 # Fin du script
103 exit 0
Voici un script qui permet de calculer le PGCD et le PPCM d'au moins deux nombres.
Il s'utilise de cette manière :
$ ./pgcd.sh 50 68 54 etc etc ...
Voici le contenu du script :
$ nl scripts/pgcd.sh
Tous les détails de la commande mcrypt
La commande mcrypt propose différentes solutions afin de l'utiliser via un script.
Soit en utilisant un fichier de configuration, soit en utilisant des variables d'environnement.
Il est possible d'utiliser un fichier de configuration qui sera utilisé par défaut par la commande mcrypt.
Ce fichier doit être obligatoirement nommé ".mcryptrc" et placé dans le home de l'utilisateur.
Il devra contenir 3 paramètres :
- l'algorithme à utiliser
- le mode d'encryptage
- la phrase de passe
$ ls -l .mcryptrc
-rw------- 1 root root 46 31 oct. 09:09 .mcryptrc
$ cat .mcryptrc
algorithm des
mode cbc
key ma_phrase_de_passe
$
Lors de l'utilisation de la commande mcrypt, les paramètres d'encryptage seront automatiquement récupérés.
$ mcrypt monFichier
Warning: It is insecure to specify keywords in the command line
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 root root 0 31 oct. 08:32 monFichier
-rw------- 1 root root 84 31 oct. 08:32 monFichier.nc
$
Il est également possible d'utiliser un fichier de configuration nommé différemment mais dans ce cas, il faudra le spécifier lors de l'utilisation de la commande mcrypt avec l'option -c.
$ ls -l my_config_file
-rw------- 1 root root 46 31 oct. 09:15 my_config_file
$ cat my_config_file
algorithm des
mode cbc
key ma_phrase_de_passe
$ mcrypt -c my_config_file monFichier
Warning: It is insecure to specify keywords in the command line
File monFichier was encrypted.
$ ls -l monFichier*
-rw-r--r-- 1 root root 0 31 oct. 08:32 monFichier
-rw------- 1 root root 84 31 oct. 08:32 monFichier.nc
$
Il est possible égalment d'utiliser des variables d'environnement qu'il faudra initialiser et exporter directement dans le script utilisant la commande mcrypt.
Ces variables sont :
MCRYPT_KEY:la phrase de passe
MCRYPT_ALGO:l'algorithme à utiliser
MCRYPT_MODE:le mode d'encryptage
Exemple d'un script :
$ cat mon_script.sh
#!/bin/bash
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export MCRYPT_KEY="la phrase de passe"
export MCRYPT_ALGO="DES"
export MCRYPT_MODE="CBC"
if [[ -e monFichier.nc ]]; then
rm monFichier.nc
fi
mcrypt monFichier
exit 0
$
Dans ce script, les variables propres à mcrypt sont initialisées et exportées.
Un test sur l'existence du fichier crypté permet de le supprimer avant d'exécuter la commande mcrypt (cela évite d'avoir un message de confirmation de la commande mcrypt pour écraser le fichier)
Enfin, la commande mcrypt est exécutée sur le fichier "monFichier".
Exécution du script :
$ ./mon_script.sh
File monFichier was encrypted.
$
Ce script peut être utilisé pour envoyer un fichier sur un serveur FTP par exemple sans compromettre les données du fichier.
Pré-requis : Installer les paquets suivants
$ apt-get install ffmpeg
$ apt-get install ubuntu-restricted-extras
$ apt-get install lame
Exécuter le script suivant :
$ cat convert.sh
#!/bin/bash
for i in *.ogg
do
j=${i%.ogg} ffmpeg -ab 192k -i "$j.ogg" "$j.mp3"
done
exit 0
Convertit tous les fichiers présents dans le dossier où le script est exécuté
Voici un script qui permet de découper un ou plusieurs mots en syllabes.
Merci à http://www.bertrandboutin.ca/Folder_151_Grammaire/P_b_division.htm pour la définition des syllabes.
Ce script peut être téléchargé ICI. ou via mon GitLab
Il s'utilise de cette manière :
$ ./syllabes.sh mot1 mot2 mot3 mot4 etc etc ...
Le script accepte au minimum un mot.
Voici le détail du script avec un maximum de commentaires :
$ cat syllabes.sh
#!/bin/bash
# Activer pour le debug
#set -x
function preRequis {
for arg in $@; do
if ! which $arg >/dev/null; then
echo "La commande $arg n'est pas installée !!!"
echo "Fin du script."
exit 1
fi
done
}
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
preRequis egrep sed
# Si le nombre d'arguments passé au script est inférieur à 1
# On quitte le script immédiatement.
if [ $# -lt 1 ]; then
echo "Usage: `basename $0` mot1 [mot2 mot3 ...]"
exit 1
fi
# Liste des voyelles
VOY="aeiouyàâäéèêëîïôöùûü"
# Liste des consonnes
CONS="zrtpqsdfghjklmwxcvbnç"
# Liste des groupes de lettres insécables
GR="bl cl fl gl pl br cr dr fr gr pr tr vr ch ph gn th"
for MOT in $@; do
# On convertit toutes les majuscules en minuscules
_MOT=$(tr "[A-Z]" "[a-z]" <<< "$MOT")
# On exécute la première règle de découpe des syllabes
# avec les groupes de lettres insécables.
for _GR in $(echo $GR); do
_MOT=$(sed "s/$_GR/-$_GR/g" <<< "$_MOT")
done
# Boucle infinie
while :; do
# Une consonne placée entre deux voyelles introduit une nouvelle syllabe.
if SYL=$(grep -E -o "[$VOY]{1}[$CONS]{1}[$VOY]{1}" <<< "$_MOT"); then
for __SYL in $SYL; do
_SYL=$(sed -r "s/^(.)(.{2})$/\1-\2/" <<< "$__SYL")
_MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
done
continue
fi
# Deux consonnes placées entre deux voyelles,
# la première appartient à la syllabe précédente,
# la seconde, à la syllabe suivante.
if SYL=$(grep -E -o "[$VOY]{1}[$CONS]{2}[$VOY]{1}" <<< "$_MOT"); then
for __SYL in $SYL; do
_SYL=$(sed -r "s/^(.{2})(.{2})$/\1-\2/" <<< "$__SYL")
_MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
done
continue
fi
# Quand il y a trois consonnes ou voyelles consécutives à l’intérieur d’un mot,
# ordinairement les deux premières terminent une syllabe,
# l’autre commence une nouvelle syllabe.
if SYL=$(grep -E -o "[$CONS]{3}|[$VOY]{3}" <<< "$_MOT"); then
for __SYL in $SYL; do
_SYL=$(sed -r "s/^(.{2})(.)$/\1-\2/" <<< "$__SYL")
_MOT=$(sed "s/$__SYL/$_SYL/" <<< "$_MOT")
done
continue
fi
# On quitte la boucle infinie si aucune des conditions précédentes n'est vraie
break
done
# On affiche le résultat
_MOT=$(sed "s/^-//" <<< "$_MOT")
echo -e "$MOT => $_MOT"
done
exit 0
Enregistrer les erreurs d'exécution d'une commande ou d'un script dans un fichier de log peut se faire tout simplement en redirigeant la sortie d'erreur standard (descripteur #2) vers un fichier :
$ cp fichierA fichierB 2>>/var/log/cp_error.log
Petit rappel :
2>/mon_fichier : Tout le contenu du fichier est écrasé
2>>/mon_fichier : Les données sont ajoutées à la fin du fichier
La consultation du fichier log :
$ cat /var/log/cp_error.log
cp: impossible d'évaluer « fichierA »: Aucun fichier ou dossier de ce type
$
C'est clair et précis. L'erreur rencontrée par la commande cp a bien été enregistrée dans le fichier.
Mais la précision pourrait être un peu plus détaillée surtout si cela concerne une commande ou un script exécuté régulièrement par une tâche cron.
Le but, obtenir un fichier de log avec la date et l'heure de l'erreur rencontrée :
$ cat /var/log/cp_error.log
ven. 24 août 2012 15:10:01 CEST --- cp: impossible d'évaluer « fichierA »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier1 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier2 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier3 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier4 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier5 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier6 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier7 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier8 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier9 »: Aucun fichier ou dossier de ce type
ven. 24 août 2012 15:12:07 CEST --- cp: impossible d'évaluer « fichier10 »: Aucun fichier ou dossier de ce type
$
C'est quand même mieux de connaitre la date et l'heure de l'erreur rencontrée.
La manière de le faire ? Avec un simple script awk.
Pré-requis :
Avoir gawk installé sur la machine.
$ dpkg -l | grep -i awk
ii gawk 1:3.1.7.dfsg-5 GNU awk, a pattern scanning and processing language
ii mawk 1.3.3-15 a pattern scanning and text processing language
$
S'il n'est pas installé :
$ apt-get install gawk
Détail du script awk :
$ cat log.awk
{
date_heure=strftime("%c",systime())
printf ("%s --- %s\n", date_heure, $0)
}
END {
exit 0
}
$
Que fait-il :
Ce script reçoit une donnée en entrée récupérée grâce à la variable $0, initialise une variable date_heure avec la date et l'heure courante grâce à la commande systime() et formatée grâce à la commande strftime puis envoi le tout sur sa sortie standard grâce à la fonction printf.
Comment l'utiliser :
Avec une commande shell :
$ cp fichierA fichierB 2>&1 >&- | /usr/bin/awk -f /root/log.awk >> /var/log/cp_error.log
La commande cp tente de copier le fichierA vers le fichierB.
La sortie d'erreur standard de la commande cp est renvoyée en premier vers la sortie standard.
La sortie standard est ensuite fermée ce qui fait que seules les erreurs sont envoyées sur la sortie standard de la commande.
La sortie standard de la commande cp est ensuite envoyée au script log.awk.
Enfin, la sortie standard du script log.awk est envoyée vers le fichier de log souhaité.
L'utilisation avec un script est aussi possible :
$ /root/mon_script.sh 2>&1 >&- | /usr/bin/awk -f /root/log.awk >> /var/log/mon_fichier.log
Si l'on souhaite envoyer la totalité des résultats d'une commande ou d'un script dans le fichier de log, il suffit de supprimer la fermeture du descripteur #1.
$ cp fichierA fichierB 2>&1 | /usr/bin/awk -f /root/log.awk >> /var/log/cp_error.log
Il n'est pas rare d'avoir sur son disque dur le même fichier présent à plusieurs endroits.
Comme des photos, par exemple ou des programmes.
Que de place perdue pour pas grand chose.
Voici un petit script qui scrute le répertoire, passé en argument, à la recherche de fichiers en double (voir plus).
Comme dans la plupart de mes scripts, vous y retrouverez la fonction "preRequis" permettant de savoir si toutes les commandes utilisées dans le script sont biens installées sur le système et la commande logger permettant de journaliser l'exécution du script dans /var/log/syslog.
Téléchargé le script ici.
Le script s'utilise de cette manière :
$ ./rechercheDoublons.sh mon_repertoire
J'utilise la commande find avec l'option -type f qui permet de rechercher tous les fichiers réguliers dans le répertoire passé en argument.
Pour chaque fichier retourné par la commande find, la commande md5sum lit le fichier en mode binaire (avec l'option -b) et écrit l'empreinte md5 du fichier dans le fichier /tmp/filesMd5sum.
Pour finir, le contenu du fichier /tmp/filesMd5sum est trié avec la commande sort, et tous les fichiers ayant la même empreinte md5 (grâce aux commandes awk '{print $1}' | uniq -d) sont affichés à l'écran.
# cat rechercheDoublons.sh
#!/bin/bash
function preRequis {
for arg in $@; do
if ! which $arg >/dev/null; then
logger -t $0 La commande $arg n\'est pas installée
echo "La commande $arg n'est pas installée !!!"
echo "Fin du script."
exit 1
fi
done
}
logger -t $0 Exécution du script
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games"
preRequis md5sum sort awk uniq grep cut find
if [ ! $# -eq 1 ]; then
echo "Erreur !!!"
echo "Utilisation : $0 répertoire"
exit 1
fi
FILE="/tmp/filesMd5sum"
REP="$1"
if [ ! -d "$REP" ]; then
echo "Erreur !!!"
echo "$REP n'est pas un répertoire."
exit 1
fi
rm -f "$FILE"
find "$REP" -type f -exec md5sum -b {} \; > "$FILE"
DOUBLON=0
for file in $(cat "$FILE" | sort | awk '{print $1}' | uniq -d); do
DOUBLON=1
echo "Empreinte MD5 identique : $file"
grep $file "$FILE" | cut -d' ' -f2
done
if [ "$DOUBLON" -eq 0 ]; then echo "Aucun doublon trouvé"; fi
logger -t $0 Fin d\'exécution du script
exit 0
Pré-requis pour que le script suivant fonctionne :
1 - Paramétrer sur le poste à sauvegarder un accès ssh au serveur de sauvegarde via un système d'authentification par clé privée / publique.
Tout est expliqué ici : http://quennec.fr/linux/utilisation/connexion-a-une-machine-distante-sans-saisir-le-mot-de-passe
2 - Le programme bzip2 doit être installé sur le poste à sauvegarder.
$ apt-get install bzip2
3 - Rendre les scripts exécutables
$ chmod +x uploadBackup.sh && chmod +x fonctions.inc.sh
Script d'archivage incrémental et transfert sftp automatique
Ce script permet d'effectuer une sauvegarde incrémentale d'un répertoire vers un serveur de sauvegarde.
Il utilise un système de niveau correspondant au jour du mois - 1.
Tous les 1er de chaque mois, une sauvegarde totale (niveau 0) est effectuée.
Une sauvegarde incrémentale est effectuée les jours suivants.
Le script est composé de 2 fichiers.
Un fichier comportant le script principal (uploadBackup.sh) et un fichier comportant les fonctions personnalisées utiles au script principal (fonctions.inc.sh).
Les fichiers à sauvegarder (variable DATADIR) sont enregistrés dans une archive CPIO compressée avec BZIP2 et stockée dans un répertoire local sur le poste à sauvegarder (variable ARCHIVEDIR).
Avant chaque sauvegarde, un fichier de niveau est créé dans le répertoire local.
Toutes les logs sont enregistrées dans un fichier stocké également dans le répertoire local.
La sauvegarde incrémentale utilise le fichier niveau précédent afin de sauvegarder tous les fichiers modifiés depuis la sauvegarde précédente grâce à la commande find et l'option -newer.
Enfin, l'archive CPIO est envoyée sur le serveur de sauvegarde (variable serveur_sauvegarde) via SFTP et stockée dans des sous-répertoires correspondant à l'année et au mois de la sauvegarde dans le dossier de sauvegarde (variable dossier_distant) .
Détail du fichier uploadBackup.sh
$ nl uploadBackup.sh
1 #!/bin/bash
2 # set -x
3 # Répertoire des scripts shell
4 SCRIPTDIR="/root/script_archivage_incremental"
5 # Répertoire des fichiers à sauvegarder
6 DATADIR="/root/dossier_a_sauvegarder"
7 # Répertoire local des archives
8 ARCHIVEDIR="/root/local_backup"
9 # Inclure les fonctions
10 . $SCRIPTDIR/fonctions.inc.sh
11 # Fichier de log
12 LOG=$ARCHIVEDIR/`getDate`.log
13 # Redirection de toutes les sorties du script vers le fichier de log
14 exec 1>$LOG 2>&1
15 # Déterminer le niveau de sauvegarde
16 # Le 1er du mois => niveau 0
17 jourDuMois=`getDayForCalcul`
18 ((niveau=$jourDuMois-1))
19 case $niveau in
20 0) # Sauvegarde totale
21 # Nettoyage du répertoire d'archive
22 rm -i $ARCHIVEDIR/*.bz2 $ARCHIVEDIR/niveau*
23 # Création du fichier de niveau (niveau 0)
24 touch $ARCHIVEDIR/niveau0
25 archive="$ARCHIVEDIR/`getDate`_niveau0.cpio"
26 find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
27 ;;
28 *)
29 # Creation du fichier de niveau
30 touch $ARCHIVEDIR/niveau$niveau
31 archive="$ARCHIVEDIR/`getDate`_niveau${niveau}.cpio"
32 # Determination du niveau precedent
33 ((niveauPrec=$niveau-1))
34 # Test si le fichier de niveau precedent existe
35 if [[ ! -f $ARCHIVEDIR/niveau$niveauPrec ]]
36 then
37 # Si il n'existe pas, sauvegarde integrale du repertoire
38 echo "Fichier de niveau $niveauPrec inexistant"
39 echo "Execution d'une sauvegarde integrale en cours de mois"
40 find $DATADIR | cpio -ocv | bzip2 -c > $archive.bz2
41 else
42 # Sauvegarde incrementale
43 find $DATADIR -newer $ARCHIVEDIR/niveau$niveauPrec | cpio -ocv | bzip2 -c > $archive.bz2
44 fi
45 ;;
46 esac
47 # Vérification de la validité de l'archive
48 if isArchiveInvalide $archive.bz2 ; then
49 echo "Archive $archive.bz2 INVALIDE - Fichier non transfere"
50 exit 1
51 fi
52 # Transfert du fichier vers le serveur de sauvegarde
53 if transfert ${archive}.bz2 ; then
54 echo "Transfert realise avec succes"
55 exit 0
56 fi
57 # Si le transfert a echoue
58 echo "Echec de transfert"
59 exit 1
$
Détail du fichier fonctions.inc.sh
$ nl fonctions.inc.sh
1 #!/bin/bash
2 function transfert {
3 typeset mois
4 typeset annee
5 # Recuperation de la valeur du premier argument passe a la fonction
6 # Recuperation du nom de l'archive a envoyer au serveur de sauvegarde
7 typeset ficATransferer=$1
8 # Adresse du serveur de sauvegarde
9 typeset serveur_sauvegarde="192.168.1.113"
10 # Compte utilise pour se connecter au serveur de sauvegarde
11 typeset user="root"
12 # Chemin absolu du dossier ou sera stocke la sauvegarde
13 typeset dossier_distant="/root/sauvegarde_dossier"
14 mois=`getMonth`
15 annee=`getYear`
16 # Test si le dossier de sauvegarde existe sur le serveur de sauvegarde
17 # Connexion au serveur avec le user root
18 ssh $user@$serveur_sauvegarde test -d $dossier_distant/$annee/$mois
19 # Test sur le code retour de la commande precedente
20 case $? in
21 0)
22 ;;
23 255)
24 echo "Echec de la commande SSH"
25 return 1
26 ;;
27 *)
28 # Si code retour different de 0 et 255
29 # Creation du repertoire de la sauvegarde
30 ssh $user@$serveur_sauvegarde mkdir -p $dossier_distant/$annee/$mois
31 ;;
32 esac
33 # Connexion au serveur de sauvegarde en FTP sécurisé
34 # Ne pas oublier les doubles chevrons << avant le mot cle FIN
35 # Ne pas mettre d'espace entre << et FIN
36 sftp -b - $user@$serveur_sauvegarde <<FIN
37 # Positionnement dans le repertoire de la sauvegarde
38 cd $dossier_distant/$annee/$mois
39 pwd
40 # Envoi de la sauvegarde
41 put $ficATransferer
42 FIN
43 # Ne pas mettre de tabulation ou d'espace devant le mot clé FIN
44 # Sinon celui-ci n'est pas reconnu
45 # Test sur le code retour de la commande SFTP
46 # Si le code retour est different de 0
47 # Une anomalie a ete rencontree
48 (( $? != 0 )) && return 1
49 # Tester si archive valide sur serveur de sauvegarde
50 ficSurMachineCible=$(basename $ficATransferer)
51 ssh $user@$serveur_sauvegarde bzip2 -t $dossier_distant/$annee/$mois/$ficSurMachineCible
52 case $? in
53 0)
54 ;;
55 255)
56 echo "Echec de la commande SSH"
57 return 1
58 ;;
59 *)
60 # Si code retour different de 0 et 255
61 # Alors l'archive est invalide
62 echo "Archive $dossier_distant/$annee/$mois/$ficSurMachineCible INVALIDE"
63 return 1
64 ;;
65 esac
66 return 0
67 }
68 function isArchiveInvalide {
69 typeset archive=$1
70 bzip2 -t $archive 2>/dev/null && return 1
71 return 0
72 }
73 function getDate {
74 date '+%Y_%m_%d'
75 }
76 function getYear {
77 date '+%Y'
78 }
79 function getMonth {
80 date '+%m'
81 }
82 function getDayForCalcul {
83 date '+%e' | sed 's/ //'
84 }
$
Quand on développe des scripts pour soi-même, toutes les commandes utilisées dans le script sont obligatoirement installées sur le système. Mais qu'en est-il si le script est partagé avec différentes personnes sur différents sytèmes ?
Il faut être sûr et certain que le script sera utilisable n'importe où et par n'importe qui.
C'est pour cette raison que j'utilise de plus en plus cette fonction dans mes scripts.
function preRequis {
for arg in $@; do
if ! which $arg >/dev/null; then
echo "La commande $arg n'est pas installée !!!"
echo "Fin du script."
exit 1
fi
done
}
Cette fonction, je l'ai appelé "preRequis". Elle reçoit des noms de commandes en arguments et vérifie à l'aide de la commande which (commande primitive du shell) l'existence sur le système de ces commandes.
Grâce à cette fonction, si une personne exécute un script contenant une commande qui n'est pas installée sur son système, un message explicite sera affiché à l'écran.
Cette fonction peut être intégrée dans n'importe quel script.
$ cat sauvegarde_differentielle.sh
#!/bin/bash
function preRequis {
for arg in $@; do
if ! which $arg >/dev/null; then
echo "La commande $arg n'est pas installée !!!"
echo "Fin du script."
exit 1
fi
done
}
preRequis nice rdiff-backup
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
exec 1>>/var/log/sauvegarde_differentielle.log
exec 2>>/var/log/sauvegarde_differentielle_error.log
nice -n 19 rdiff-backup /repertoire_a_sauvegarder /destination_de_la_sauvegarde && nice -n 19 rdiff-backup --remove-older-than 1W --force /destination_de_la_sauvegarde
exit 0
$
Dans cet exemple, j'utilise la fonction preRequis pour vérifier que les commandes nice et rdiff-backup sont bien installées sur le système.
Voici un script qui se charge de vérifier les différences qu'il peut y avoir sur une page web entre 2 périodes de temps définies.
$ nl check_url.sh
1 #!/bin/bash
2 export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
3 URL="http://quennec.fr"
4 PREC="/root/precedent"
5 SUIV="/root/suivant"
6 exec 1>/dev/null 2>&1
7 if [ -e $SUIV ]; then
8 rm $SUIV
9 fi
10 curl $URL > $SUIV
11 if [ -e $PREC ]; then
12 diff $SUIV $PREC
13 if [ $? == 1 ]; then
14 mail -s "Ecart sur la page $URL" ronan@quennec.fr<<EOF
15 EOF
16 fi
17 fi
18 if [ -e $PREC ]; then
19 rm $PREC
20 fi
21 mv $SUIV $PREC
22 exit 0
$
Lignes 2 à 5 : initialisation des variables utilisées dans le script.
Ligne 6 : Redirection des entrées et sorties standards.
Ligne 7 à 9 : Suppression du fichier $SUIV si celui-ci existe.
Ligne 10 : Téléchargement du contenu de la page web et enregistrement dans le fichier $SUIV
Ligne 11 à 17 : Si le fichier $PREC existe, le programme diff compare les 2 fichiers $SUIV et $PREC. Si il y a des différences ($? == 1), un mail est envoyé à l'adresse indiquée.
Ligne 18 à 20 : Si le fichier $PREC existe, il est supprimé.
Ligne 21 : Le fichier $SUIV est renommé en $PREC.
Rendre le script exécutable :
$ chmod +x check_url.sh
Reste à exécuter le script
$ ./check_url.sh
Pour l'automatisation, une ligne sera ajoutée à crontab :
$ crontab -e
*/5 * * * * /root/check_url.sh
Le script sera exécuté toutes les 5 minutes.
Fichier attaché | Taille |
---|---|
check_url.sh_.txt | 460 octets |