Quelques scripts Unix
Recherche de processus
Ce script simple adapte la commande ps à des systèmes
d'exploitation, car les paramètres de cette commande changent suivant
les systèmes. Toute personne travaillant sur plusieurs systèmes est
rapidement agacée par des comportements de commandes qui diffèrent
suivant la machine à qui il s'adresse.
Ce script permet de connaître les informations sur les processus
associés à des commandes lancées par l'utilisateur. Ces informations
sont extraites de la commande ps, en ne gardant que la partie concernant
la commande passée en paramètre.
Par exemple 'exec_rproc ksh ' fournit l'affichage:
F S UID PID PPID C P TTY TIME CMD
40001 A 212 34636 34898 0 - 0:00 ksh
240001 A 212 34898 33896 0 - 0:00 ksh
240001 A 212 35608 35358 0 pts/3 0:00 ksh
Le script commence par vérifier la présence d'un paramètre ou
de l'option -h en première position.
#! /bin/sh
# exec_rproc : recherche processus
sex=`uname` # le système d'exploitation
id=`logname` # le nom de l'utilisateur
# 1. Au moins un paramètre; option -h: demande d'aide
if [ $# = 0 ] || [ "$1" = '-h' ] ; then
echo \* \*
echo \* \* Recherche de processus : rp nom_executable
echo \* \* ' rp "mat*" #' processus commençant par mat
echo \* \*
exit 1
fi
Ensuite on adapte le script aux paramètres de la commande "ps" qui ne
sont pas les mêmes suivant la version d'unix utilisée ("SunOS",
"Linux" ou "AIX").
# 2. Adaptation au système d'exploitation
# sur SunOS ps | grep "mat*"
# sur Linux ps -xu | grep "mat*"
# sur AIX ps -xu astier| grep "mat*"
if [ $sex = Linux ]; then
# echo 'USER PID %CPU %MEM SIZE RSS TTY STAT START TIME COMMAND'
cmd_ps='ps -xu '; coupe="-c1-20,30-"; fil=.
elif [ $sex = SunOS ]; then
# echo ' UID PID PPID C STIME TTY TIME CMD'
cmd_ps='ps -ef '; coupe=-c1-22,39-; fil=`logname`
elif [ $sex = AIX ]
then
# echo 'F S UID PID PPID C PRI NI ADDR ... TTY TIME CMD'
cmd_ps="ps -lu $id "; coupe="-c1-32,61-"; fil='.'
fi
Enfin la commande "ps" et l'extraction des résultats sont effectués.
Il faut remarquer l'utilisation de "egrep" pour garder à la fois la
première ligne de l'affichage fourni par "ps" et les lignes concernant
la commande.
# 3. Commande effective
$cmd_ps | cut $coupe | egrep "PID|$fil" | egrep "PID|$1" | grep -v egrep
Peut-on créer ou écraser un fichier ?
Dans certaines situations de création de fichier, on veut
éviter d'écraser un fichier de même nom, qui existe déjà, et dont on
avait oublié l'existence.
Le script ci-après teste l'existence et la possibilité de créer un fichier, dont
le nom est passé en paramètre. Il renvoie, suivant le cas:
1 : si le fichier existe et l'utilisateur répond non à la
demande d'écrasement
2 : si le fichier existe et n'est pas modifiable
3 : si le fichier n'existe pas, mais le répertoire
n'est pas modifiable
4 : si le script est utilisé avec 0 ou plus d'un
paramètre
0 : dans les autres cas
# autoriseCreation.sh (31 mars 2004)
# autoriseCreation nomFichier
fic= # fichier étudié (premier paramètre)
# 1. Quelques constantes
NSCRIPT=`basename $0` # nom du script
_echo() { # adapté aux séquences d'échappement
if [ `uname` = Linux ]; then echo -e $*
else echo $*
fi
}
GRAS="\\033[1;31;47m" # 31:rouge 34: bleu
FIN="\\033[0m"
# 2. Vérifier l'existence d'un paramètre
if [ $# -ne 1 ]; then
_echo $GRAS
_echo "+"
_echo " + ! ! ! Il faut un paramètre"
_echo " + `basename $0` fichier"
_echo " + exemple `basename $0` resultat"
_echo "+"$FIN
exit 4
fi
fic=$1
# 3. Traitement effectif
if [ -s $fic ] && [ -f $fic ]; then # fichier ordinaire, non vide
if [ -w $fic ]; then
_echo "Voulez-vous écraser $fic (oui/non) ? \c"
read rep
if [ "$rep" != oui ]; then exit 1; fi # utilisateur ne veut pas
else exit 2 # fichier protégé en écriture
fi
elif [ ! -w `dirname $fic` ]; then exit 3; # répertoire non modifiable
fi
Les gros fichiers
Ce script donne les fichiers d'une arborescence dont la taille dépasse
une valeur donnée, exprimée en bloc (512 octets) ou en kilo.octets.
Il est utilisé avec deux paramètres: le nom d'un répertoire et une
indication de taille (qui est: un entier positif éventuellement suivi
de k, pour indiquer une taille en octets).
Voici un exemple d'affichage souhaité:
+ Répertoire divers
TAILLE NOM (taille en k octets)
1584 divers/cpp/coursCpp.pdf
3672 divers/gvim61.exe
3736 divers/julie/atelier.zip
+ 3 fichier(s) d'au moins 1000k
Exemples de commandes:
grosFic courrier 1000k
grosFic ~/exec 300
Le premier appel demande les fichiers de plus d'un méga.octets; le
second demande les fichiers de plus de 300 blocs.
On verra ci-dessous que la vérification du
deuxième paramètre est effectuée
par deux expressions régulières; la première demande une valeur
uniquement constitutée de chiffres; la deuxième impose la présence de k
après les chiffres:
if (echo $2 | grep "^[1-9][0-9]*$" > /dev/null ) ||
(echo $2 | grep "^[1-9][0-9]*k$" > /dev/null )
L'introduction du script indique les paramètres, et définit quelques
constantes; puis les vérifications sur les paramètres sont effectuées,
avec la prise en compte des deux possibilités d'indiquer la taille, soit
en bloc (elle est alors convertie en kilo) soit en kilo actets.
# grosFic.sh (19 mars 2004)
# Fichiers de grande taille
# grosFic.sh ~ 1000 # fichiers de plus de 1000 blocs
# grosFic.sh ~ 1000k # fichiers de plus de 1000k octets
rep=$1 # nom du répertoire de départ
tk=$2 # taille en k octets (100t)
# # # # # # # # # # # # # Quelques constantes # # # # # # # # # # # # #
NSCRIPT=`basename $0` # nom du script
# Affichage, avec interprétation des "séquences escape"
A_=echo; if [ `uname` = Linux ]; then A_='echo -e'; fi
# Attributs d'affichage; marque de séquence [, soit "\\033["
GRAS="\\033[1;31;47m" # rouge sur fond blanc
FIN="\\033[0m" # fin
# # # # # # # # # # # # # Vérifications # # # # # # # # # # # # #
# Vérifier 2 paramètres
if [ $# -ne 2 ]; then
$A_ $GRAS
echo "+"
echo " + ! ! ! Il faut deux paramètres"
echo " + `basename $0` repertoire taille_k_octets"
echo " + exemple ie03.sh divers 100k"
$A_ "+$FIN"
exit 1
# Vérifier premier paramètre est un répertoire
elif ! [ -d $1 -a -r $1 -a -x $1 ]; then
$A_ $GRAS
echo "+"
echo " + ! ! ! $1 doit être un répertoire accessible"
$A_ "+$FIN"
exit 2
fi
# Deuxième paramètre: il commence par un chiffre, puis 88 ou 88k
# et convertir un nombre de blos en k.octets
if (echo $2 | grep "^[1-9][0-9]*$" > /dev/null ) ||
(echo $2 | grep "^[1-9][0-9]*k$" > /dev/null )
then
case "$2" in
[0-9]*k) tk=$2 ;;
[0-9]*) let tk=$2/2; tk=${tk}k;;
esac
else
$A_ $GRAS
echo "+"
echo " + ! ! ! $2 est une valeur numérique incorrecte";
$A_ "+$FIN"
exit 3;
fi
Le traitement proprement dit demande à la commande
find de trouver les fichiers dont la taille dépasse la valeur
fournie; puis le nombre de fichiers est évaluée et l'affichage des
fichiers, avec leur taille, est effectué par la commande ls.
# # # # # # # # # # # # # Traitement # # # # # # # # # # # # #
# fics = les noms de fichiers dont la taille dépasse $tk
fics=`find $rep -type f -size +$tk`;
# nbf = nombre de fichiers
nbf=`echo $fics | wc -w | tr -s ' ' `;
# Pas de fichier, ou afficher par ls, les noms et taille
if [ $nbf -eq 0 ]
then
$A_ "+ + ${GRAS}Aucun fichier$FIN n'a une taille supérieure à $tk"
else
echo "+"
echo " + Répertoire $rep (taille en k octets):"
$A_ "${GRAS}TAILLE$FIN ${GRAS}NOM$FIN"
ls -s1 $fics
$A_ " + $GRAS$nbf fichier(s)$FIN d'au moins $tk"
echo "+"
fi
Mot dans quel fichier source ?
Ce script permet la recherche de mots dans des fichiers sources
en C++, c'est-à-dire dont l'extension est .h, .cpp, .cc ou .C.
Voici un d'appel, et les résultats fournis:
dansSourceC ../cpp rat.h
./rat/rat.C:3:#include "rat.h"
./rat/testop.C:3:#include "rat.h"
+ + A partir de /dptinfo/users/astier/app/cpp, 'rat.h' trouvé dans 2 fichiers
+ + ./rat/rat.C ./rat/testop.C
Les paramètres du script sont un nom de répertoire et un mot.
Les premières lignes du script introduisent le sens des paramètres et
quelques constantes utilisées.
# dansSourceC (19 mars 2004) Recherche de fichiers C contenant un mot
# dansSourceC . INT_MAX
# dansSourceC ../C++ INT_MAX
rep=$1 # répertoire à explorer
mot=$2 # mot à chercher
EXTENSIONS="h cpp C cc" # extensions des fichiers parcourus
# # # # # # # # # # # # # Quelques constantes # # # # # # # # # # # # #
NSCRIPT=`basename $0` # nom du script
# Affichage, avec interprétation des "séquences escape"
A_=echo; if [ `uname` = Linux ]; then A_='echo -e'; fi
# Attributs d'affichage; marque de séquence <ESC>[, soit "\\033["
GRAS="\\033[1;31;47m" # rouge sur fond blanc
FIN="\\033[0m" # fin
Puis on effectue des vérifications sur le nombre de paramètres et la
validité du premier paramètre, qui doit être un répertoire qui puisse
être parcouru (droits "rx").
# # # # # # # # # # # # # Vérifications # # # # # # # # # # # # #
# Vérifier 2 paramètres
if [ $# -ne 2 ]; then
$A_ "$GRAS"
echo "+"
echo " + ! ! ! Il faut deux paramètres"
echo " + $NSCRIPT répertoire mot"
echo " + Exemple $NSCRIPT divers TabDisp"
$A_ "+ $FIN"
exit 1
# Vérifier paramètre est un répertoire
elif ! [ -d $1 ]; then
$A_ "$GRAS"
echo "+"
echo " + ! ! ! $1 doit être un répertoire"
$A_ "+$FIN"
exit 2
# Vérifier droits rx sur $1
elif [ ! -r $1 ] || [ ! -x $1 ]; then
$A_ "$GRAS"
echo "+"
echo " + ! ! ! $1 doit être un répertoire accessible"
$A_ "+$FIN"
exit 3
fi
Enfin le traitement, qui commence par établir la liste de tous les
fichiers (variable fics ) qui contiennent le mot cherché,
en examinant successivement chaque extension étudiée.
Si le nombre de fichiers trouvés est non nul, on affiche chaque ligne
contenant le mot recherché.
# # # # # # # # # # # # # Traitement # # # # # # # # # # # # #
cd $rep
# Liste des noms de fichiers, pour chaque extension, contenant $mot
fics=""; # cumul des noms de fichiers
for e in $EXTENSIONS
do
nf=`find . -type f -name "*.$e" -exec grep -wl "$mot" {} \;`
fics=$fics" "$nf
done
# Nombre de fichiers trouvés
nfic=`echo $fics | wc -w | tr -s ' '`
# Afficher nom de fichier et lignes contenant $mot
if [ $nfic -eq 0 ] ; then
$A_ "+ + '$mot' ${GRAS}non trouvé${FIN} à partir de $PWD"
else
# Afficher les lignes contenant $mot
grep -wn "$mot" $fics
# Remplacer séparateur \n par espace
fics=`echo $fics | tr '\n' ' '`
echo +
$A_ "+ + A partir de $PWD, '$mot' ${GRAS}trouvé dans $nfic fichiers${FIN}"
echo "+ + $fics"
echo +
fi
#
Envoi de fichiers par courriers
Cet exemple met en avant:
- l'utilisation d'une fonction, avec ses variables locales et les
variables partagées avec le script
- une vérification syntaxique simple d'une adresse électronique
- une utilisation de "constante", par la commande "typeset -r"
- la possibilité d'exécuter partiellement le script suivant que la
variable MAP, indicateur de mise au point, est ou
non, définie avant l'appel du script.
- un envoi de courrier, avec pièce attachée, par la commande mail
Ce script permet d'envoyer plusieurs fichiers par courrier électronique;
le destinataire reçoit un fichier au format "tar.gz". Exemple
d'utilisation:
envoi_fichiers toto@machine.fr *.h *.C
S'il n'y a pas de paramètres le script affiche un bref mode d'emploi.
!/bin/sh
# exec_ef envoi de fichier(s) groupés et compressés
# exemples MAP=OUI exec_ef $ADR4 tmp exec*
# exec_ef $ADR4 tmp exec*
# 1. Principales variables
adr= # adresse du destinataire
fics= # fichiers à regrouper et compresser
nomfic=lesfics.gz # nom de la pièce attachée
TMP=/tmp/tmp.$LOGNAME # un fichier temporaire
Ensuite on définit quelques informations qui facilitent la programmation du
script et l'affichage. En particulier on définit la date et l'heure
courante, et $a_ comme étant la
commande echo, avec interprétation des suites d'échappement (les
options sont différentes sur "Linux" ou "AIX").
# 2. Quelques informations: NSCRIPT, DATE, HMS, GRAS, FIN
function ladate {
# calcule DATE et HMS
local jour quant qmois annee
date +"%w %d %m %Y %T" | read jour quant qmois annee HMS
qmois=12
# Quel jour $1 ?
case $jour in
0) jour=dimanche;;
1) jour=lundi;;
2) jour=mardi;;
3) jour=mercredi;;
4) jour=jeudi;;
5) jour=vendredi;;
6) jour=samedi;;
esac
# # variable de type tableau pour le mois
set -A mois janvier février mars avril mai juin juillet aout \
septembre octobre novembre décembre
DATE="$jour, $quant ${mois[$qmois-1]} $annee"
}
#
ladate # calcule DATE et HMS (hh:mm:ss)
NSCRIPT=`basename $0` # nom du script
# pour la couleur
DSQ="\\033["; FIN=$DSQ"0m"; GRAS=$DSQ"1;31;47m" # 31:rouge 34: bleu
a_=echo # commande d'affichage
if [ `uname` = LINUX ]; then a_='echo -e'; fi
# mode mise au point (variable MAP définie et non vide)?
if [ ${#MAP} -gt 0 ]; then MAP=":" ; else MAP="! :"; fi
# les constantes
typeset -r MAP NSCRIPT FIN GRAS DATE HMS
Puis on vérifie qu'il y a au moins deux paramètres.
# 3. Erreur ou aide
if [ $# -lt 2 ]; then
$a_ $GRAS+
$a_ " + Envoi d'un ou plusieurs fichiers compressés"
$a_ " + $NSCRIPT adresse_électronique fichier_1 ..."
$a_ ' +'
$a_ " + Exemple: $NSCRIPT astérix@uderzo.com fic1.html fic2* "
$a_ +$FIN
exit 1
fi
On vérifie également que le premier paramètre est une adresse
personnelle, c'est à dire contient un '@' et un nom de machine
terminée par '.xx' ou '.xxx'.
# 4. Récupérer l'adresse, l'enlever de la liste et la vérifier
adr=$1
shift
# commence par une lettre, contient @: "^[A-Za-z].*@"
# termine par x.yy ou x.yyy: "[.][a-z]\{2,3\}$"
if ! echo $adr|grep "^[A-Za-z].*@"|grep "[^.][.][a-z]\{2,3\}$">/dev/null
then
# adresse incorrecte
$a_ $GRAS
$a_ " ! ! ! Adresse incorrecte: $adr"
$a_ $FIN
exit 2;
fi
On vérifie que tous les noms de fichiers, qui figurent après l'adresse,
existent.
# 5. Vérifier que tous les fichiers existent
fics=$*
if ! ls $fics > /dev/null 2>&1; then
$a_ $GRAS # un message par fichier
for i in $fics; do
if ls $i > /dev/null 2>&1 ; then :
else
$a_ "+ + fichier $i introuvable"
fi
done
$a_ " ! ! ! PAS de courrier envoyé"
$a_ $FIN
exit 3;
fi
Si la variable MAP est une chaîne non vide, on arrête le script; cela
permet de faire des essais sans envoyer effectivement du courrier.
# 6. Arrêt si on est en phase de mise au point
if $MAP ; then
$a_ "+ + Pas d'envoi des fichier(s) $GRAS $fics$FIN"
$a_ "+ + à $GRAS $adr$FIN ($DATE, $HMS)"
exit 0
fi
Enfin le traitement proprement dit est effectué. Tous les fichiers sont
regroupés en un seul (par tar), qui est compressé (par gzip), puis recodé
(par uuencode), pour n'avoir que des octets où le premier bit est à zéro.
Le courrier est alors écrit avec ce fichier de nom $TMP.gz.asc
en attaché.
# 7. Création et envoi du courrier
tar -cf $TMP $fics # regrouper (crée ttt)
gzip -f $TMP # compresser (crée ttt.gz
uuencode $TMP.gz $nomfic > $TMP.gz.asc # convertir pour code 7 bits
cat << FIN. | mail -s"le $DATE, $HMS" $adr
Ce jour: $DATE ($HMS), envoi de $fics
mail -s"le $DATE" $adr
~r $TMP.gz.asc
FIN.
#
$a_ "+ + Envoi des fichier(s) $GRAS $fics$FIN"
$a_ "+ + $GRAS $fics$FIN"
$a_ "+ + à $GRAS $adr$FIN ($DATE)$FIN"
Quelques liens