[gull] Truc et astuces: bash, sed et eval, partie 2: variables dynamiques
Félix Hauri
felix at f-hauri.ch
Wed Nov 16 00:52:09 CET 2011
Bonjour les hackers!
On en remet une couche?
On Sun, Nov 06, 2011 at 06:11:43PM +0100, Arnaud wrote:
>
> J'avais un peu utilisé eval pour utiliser des noms de variables
> dynamiques, mais ça m'avait posé quelques soucis en bash ...
En préambule, je dirais qu'il faut faire attention, en jouant avec les
variables d'environnement, de ne pas modifier des variables utiles!
set | less
...
Pour supprimer des variables, deux solutions: ``unset'' ou ``exit'' (Ctrl-D):
.1: unset sert à supprimer des variables du shell courant;
$ set | less
$ ds='/^ %[^%]* /{s/^ *%\([^ ]*\) .*$/DATE_\1=\"%\1\"/;::;s/\([^:%]\):/\1_/;t:;p}'
$ eval $(date +"$(date --help | sed -ne "$ds")")
$ set | less
$ unset $(set | grep ^DATE_ | cut -d= -f1 )
$ set | less
.2: les variables ne peuvent pas être exportées vers le parent:
$ set | less
$ /bin/bash
$ set | less
$ ds='/^ %[^%]* /{s/^ *%\([^ ]*\) .*$/DATE_\1=\"%\1\"/;::;s/\([^:%]\):/\1_/;t:;p}'
$ eval $(date +"$(date --help | sed -ne "$ds")")
$ set | less
$ exit
$ set | less
Voici, par exemple comment je repartitionne une clef usb, sur laquelle est
installé Debian-Live avec une 2ème partition ``live-rw''. Je supprime
la 2ème partition, agrandi la 1ère et créé 2 partition sur l'espace
restant:
* !!! En root, à vos risques et périls, donc! !!! *
D'abord quelque variables de base:
# STICK=sdX # (ma clef usb)
# UBUNTU=/tmp/ubuntu/ubuntu-11.10-desktop-i386.iso
( téléchargé à l'aide du script: http://f-hauri.ch/vrac/get_ubuntu.sh ;-)
La taille en Mo (Mibi) du CD ubuntu
# UBUSIZE=$(stat -c 1+%s/2^20 $UBUNTU | bc )
Là, je définis trois variables (en Mibi égallement): TOTSTICK taille tot
du stick, FIRST première partition et STICKNAME le nom hardware du stick:
# eval $(
parted -sm /dev/$STICK 'unit Mi' 'print' |
sed -ne '
s/^1:[^:]*:\([0-9]*\)Mi.*$/FIRST=\1/p;
s/^\/[^:]*:\([0-9]*\)Mi.*:msdos:\(.*\);$/TOTSTICK=\1 STICKNAME="\2"/p;
')
La première partitions doit donc être redimensionnée à (+3%):
# NEWFIRST=$(( ( FIRST+UBUSIZE)*34/33 ))
Le seconde doit utiliser la moitié du reste et la troisième, le reste.
# SECOND=$(( (TOTSTICK-NEWFIRST)/2 +NEWFIRST ))
La commande suivant *SUPPRIME* la 2ème partition (ATTENTION aux
données perdues en cas de mauvaise application de ce qui suit!)
# parted -sm /dev/$STICK 'unit Mi' \
"resize 1 0 $NEWFIRST" \
"mkpart primary ext3 $NEWFIRST $SECOND" \
"mkpart primary ext3 $SECOND $TOTSTICK" \
print
# sync
# sleep .5
Et on formatte les partitions 2 et 3 labellées resp. live-rw et casper-rw
# for NUM_LAB in 2:live-rw 3:casper-rw ;do
echo mkfs.ext3 -L ${NUM_LAB#*:} /dev/${STICK}${NUM_LAB%:*}
done
Maintenant, voici une ``véritable application'' utilisant des variables
dynamiques (calcul statistique sur syslog, compte les occurences et
affiche les premier et dernière apparitions, pour chaque daemon.):
Tout d'abord, il nous faut une copie de /var/log/syslog:
$ sudo cat /var/log/syslog >/tmp/syslog
$ while read -a line ;do
nam=${line[4]%[*}
nam=${nam##*/}
nam=$(echo ${nam%:*}|
tr A-Z\\000-@ a-z_)
nam=${nam##_}
nam=${nam%%_}
eval "[ \"\$FRST_$nam\" ] || FRST_$nam=\"${line[@]:0:3}\""
eval "LAST_$nam=\"${line[@]:0:3}\""
eval CUNT_$nam=$((CUNT_$nam+1))
eval "printf \"\r%-25s %-16s %-16s %5d\" $nam \"\$FRST_$nam\" \
\"\$LAST_$nam\" \$CUNT_$nam"
[ "$ALLVARS" == "${ALLVARS%$nam*}" ] && ALLVARS="$ALLVARS $nam"
done < /tmp/syslog ;echo
Bon, en shell, ce n'est pas instantanné!
plus de 5 minutes pour un fichier de 35'ooo lignes...
$ printf "%s\n" $ALLVARS | sort | column
Et pour finir:
$ for VNAME in $ALLVARS;do
eval printf \"%-25s %-16s %-16s %5d\\n\" $VNAME \"\$FRST_$VNAME\" \
\"\$LAST_$VNAME\" \$CUNT_$VNAME
done | sort -nk8
ou (sans ``-nk8'')
$ ... | sort
(Nota: Ce petit exercice pour évaluer la possibilité de créer et nommer des variables
à la volée... En shell, cette opération n'est pas rentable. Il est probable qu'en
exploitant mieux les finesses de Bash, on améliore le score, mais dans l'absolu,
ce genre de truc est plus vite pondu en perl et nettement plus rapide:
$ perl </tmp/syslog -e ' my %logs;
while (<>) {
/^(\S+\s+\S+\s+\S+)\s+\S+\s+(\S*\/|)([^:\/\[ ]+)[:\[ ].*/ && do {
(my $nam=$3)=~y|A-Z|a-z|;
$logs{$nam}->{"last"}=$1;
$logs{$nam}->{"first"}=$1 if 1 > $logs{$nam}->{"count"}++
};
};
map { printf "%-25s %-16s %-16s %5d\n", $_, $logs{$_}->{"first"},
$logs{$_}->{"last"},$logs{$_}->{"count"}
} sort { $logs{$a}->{"count"} <=> $logs{$b}->{"count"} } keys %logs'
environ 0,2 sec à l'execution, sur le même fichier et le même poste. ;)
--
Félix Hauri - <felix at f-hauri.ch> - http://www.f-hauri.ch
More information about the gull
mailing list