Scripting

De PiX-Mania Wiki.

Sommaire

Attention

Faire des scripts, c'est bien, mais garder ceci à l'esprit :

  • Bash n'est pas prévu pour faire de la sécurité
    • en 2 seconces, il est possible de faire un déni de service (http://fr.wikipedia.org/wiki/Fork_bomb)
    • Bash peut s'avérer plus complexe et moins bien pensé (bash fait un "eval" lors de l'assignation d'une valeur à une variable... il est donc possible de craquer la machine via cette variable en placant certaine chose dedans :
CRACK_VAR="\"`rm /etc/`\""

(le simple fait de régler cette variable met le système à genoux!!!...)

  • Bash est plus prévu pour des petites tâches à exécuter une fois la sécurité vérifiée
  • pour faire un script de sécurité, utiliser plutôt un meilleur language du genre "perl" ou "python"


Les boucles

for

for DIR in bin boot etc home lib opt root sbin usr var
do
    echo "> upload of $DIR"
 
    lastArchive=`ls -t $BACKUP_DIR/*-$DIR-*.tar.gz | sed 'q'`       #get the last archive
    lastArchiveMD5=`ls -t $BACKUP_DIR/*-$DIR-*.md5 | sed 'q'`       #and its MD5
 
    lftp -u user,pass sftp://URL -e "cd /mnt/data/backup/system/serveur; put $lastArchive; put $lastArchiveMD5; bye"
done

Cet exemple vous montre une boucle "for" qui va boucler plusieurs fois pour parser les paramètres situé après la directive "in" (bin, boot, etc, ...). Dans la boucle, la variable "DIR" contiendra le premier élément au premier passage (bin) et le dernier au dernier passage (var) ... tout est logique.


while

while true
do
     echo "I love UDP ..."
done

Une boucle infinie ... oui, ca ne sert à rien, sauf si vous regardez l'exemple suivant :


/etc/init.d/ntop restart ; while true; do ps aux | grep ntop && sleep 1 ; done

Cet exemple est un bon exemple pratique. J'ai des soucis avec le logiciel Ntop (qui est un service). Le processus disparait après un temps (5 min grand max). Pour vérifier si ca fonctionne, je fais tout en 1 ligne :

  • redémarrage de ntop (la, le processus est lancé)
  • je fais une boucle infinie :
    • ou je demande une liste des process
    • et ou je n'affiche que les lignes parlant de "ntop"
    • j'y ajoute un sleep d'une seconde (sinon, boucle infinie et processeur à 100%)

Très pratiqueImage:Wink.gif ... Avec un peu d'imagination, il est très simple de monitorer l'utilisation de vos disques...


Les conditions

if [ ! -f /tmp/system.backup ]    
then                              
     date > /tmp/system.backup 
else                              
     echo "> a backup is already running! (/tmp/system.backup)"
     return                                                    
fi

Cet exemple est un "si" (bien connu de la progra ...). Le paramètre "-f" permet de vérifier si la cible (paramètre suivant) existe et est bien un fichier. Dans le cas contraire, renvoie non sinon oui. Dans cet exemple, il y a une négation "!" qui permet d'inverser la condition. Si le fichier n'existe pas ou n'est pas un fichier, on le crée. Dans le cas contraire (il existe déjà), on peut en conclure que le script tourne déjà (car ce fichier est supprimé en fin de script .. ce fichier fait donc office de mutex). Le "fi" de fin permet de préciser la fin du bloque "if".


Plusieurs conditions

if [[ ! -f $lastBackupFile || $DATE_TEST -gt `stat -c %Y $lastBackupFile` ]]
then
   ....
else
   ....
fi

Ici, on utilise 2 conditions dans un seul if. Noter les doubles crochets à gauche et à droite ainsi que le double pipe (OR). Dans cet exemple, on entre dans le bloque "then" si le fichier référencé dans la variable "lastBackupFile" n'existe pas ou n'est pas un fichier OU si une date (DATE_TEST) est supérieur à la date de modification du ce meme fichier (la deuxième condition vérifie que l'archive n'a pas plus d'une semaine; dans le cas contraire, il faut entrer dans le bloque "then" pour créer une nouvelle archive.


Les différentes conditions

vérifier qu'un répertoire existe

if [ -d "/mnt/data1/$INSTANCE" ]; then
    echo "> The directory exists"
else
    echo "> Error: the directory doesn't exists"
fi

Switch case

Voici un exemple :

case "$SSH_ORIGINAL_COMMAND" in
*\&*|*\|*|*\(*|*\{*|*\<*|*\>*|*\;*|*\`*)
echo "Sorry, your command '$SSH_ORIGINAL_COMMAND' has been rejected by the validation script ..."
;;
hostname)
$SSH_ORIGINAL_COMMAND
;;
sudo\ service\ jboss-*)
echo "> JBoss service accepted"
$SSH_ORIGINAL_COMMAND
;;
java*)
echo "> Java VM accepted"
$SSH_ORIGINAL_COMMAND
;;
*)
echo "Sorry, your command '$SSH_ORIGINAL_COMMAND' has been rejected by the validation script ..."
;;
esac

Ce "case" va permettre de refuser l'exécution d'une commande (contenue dans "$SSH_ORIGINAL_COMMAND"). Noter que dans un "case", on peut mettre des "OR" (caractère pipe => "|") et probablement des AND et autre ...


Note: en place un "*" à la fin => c'est le "default case"...


Les dates

Récupérer la date du jour

serveur script # date '+%Y%m%d'
20081120


Récupérer la date du jour et l'heure

[laurent@wk01lhe ~]$ date '+%Y%m%d_%H:%M:%S'
20090917_09:49:44

Récupérer le nombre de secondes écoulées depuis Epoch

loop ~ # date +%s
1228769324


La date de modification d'un fichier en secondes depuis Epoch

loop test # stat -c %Y a.txt
1228771815

Les fichiers texte (ou les flux)

Récupérer la première ligne

loop ~ # ls /usr/bin | head -n 1
[

ou

loop ~ # ls /usr/bin | sed 'q'
[

ou

loop ~ # ls /usr/bin | sed -n "1 p"
[


Récupérer la dernière ligne

loop ~ # ls /usr/bin | tail -n 1
zsoelim


Récupérer le fichier, mais sans les lignes qui commencent par '#' et sans les lignes vide

On sélectionne les lignes qui commencent par '#' et '$', puis on inverse la sélection avec le paramètre "-v" :

serveur # egrep -v '^(#|$)' /etc/postfix/main.cf
queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix
mail_owner = postfix
myhostname = serveur.loopx.dyndns.org
unknown_local_recipient_reject_code = 550
relayhost = relay.skynet.be


debug_peer_level = 2
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         xxgdb $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
html_directory = /usr/share/doc/postfix-2.4.6-r2/html
manpage_directory = /usr/share/man
sample_directory = /etc/postfix
readme_directory = /usr/share/doc/postfix-2.4.6-r2/readme
home_mailbox = .maildir/

Récupérer les X premiers caractères d'une ligne

Dans mon cas, c'est une longueur fixe et donc, pas besoin d'utiliser des délimiteurs. Il me faut simplement les 7 premiers caractères d'une commande qui sera mise dans une variable (cette commande contient un nom d'instance).

srv0909[0][root@dj0160 ~]$ echo "srv0909 status" | head -c 7 | xargs echo
srv0909
  • echo "srv0909 status" : permet de simuler une ligne identique à ce qui pourrait se produire
  • head -c 7 : va prendre les 7 premiers octets (caractères)
  • xargs echo : va prendre la sortie de "head" (7 caractères) et placer ces caractères après le "echo" => "echo <7caractères>" exécuté via "xargs"


Récupérer le mot numéro X dans une ligne

Ici, on a une ligne qui contient plusieurs mot. On veut, par exemple, seulement le premier mot :

[0][root@dj0160 ~]$ echo "srv0909 status" | cut -d ' ' -f 1
srv0909
  • echo ... : c'est la simulation d'une ligne
  • "cut -d ' ' -f 1" : va utiliser le séparateur ' ' (=espace) pour découper la ligne en mot et va en sortir uniquement le premier (option "-f")


Un "cat * | grep <quelque_chose>" qui affiche les noms des fichiers parcouru

Tout simplement :

for i in `ls *` ; do echo "$i:" && cat $i | grep <quelque_chose> ; done


Ce qui donne (exemple avec nagios) :

base.cfg:
cgi.cfg:
checkcommands.cfg:
command-plugins.cfg:
groups.cfg:
        members         <quelque_chose>
hosts.cfg:
# <quelque_chose>
        host_name       <quelque_chose>
htpasswd:
misccommands.cfg:
nagios.cfg:
nrpe.cfg:
resource.cfg:
services.cfg:
suidcheck.ini:
lib::
cat: lib:: No such file or directory
cgi:
cat: cgi: No such file or directory
plugins:
cat: plugins: No such file or directory

Lecture des inputs

Simple, la commande "read" est la pour ca.


le nom d'utilisateur

read -p "Entrer le nom d'utilisateur : " user

La saisie (en claire) sera enregistré dans la variable "user".


le mot de passe

Un rien plus complexe : il faut éviter les echo ... :

stty -echo
read -p "Please provide the password : " PASSWD
stty echo

"stty" permet justement d'éviter les echoImage:Wink.gif


Quelques variables spéciales

echo $$            #affiche le pid du processus du votre script (sh, bash, ksh, ...)
echo $?            #affiche le code de retour de la commande précédemment exécutée
echo $@            #affiche tous les paramètres passés au script

Liens

Les expressions régulières

Outils personnels