Scripting
De PiX-Mania Wiki.
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 pratique
... 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 echo
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
