Groupe d'Utilisateurs de Logiciels Libres
de Nancy et ses environs

MirabellugWiki

Csh

PagePrincipale :: DerniersChangements :: ParametresUtilisateur :: Vous êtes 38.107.191.91

Introduction


Le but du jeu est ici est de réaliser un petit script csh pour Debian. Ce script va automatiser la recherche de paquets dont aucun autre ne dépend. Outre des programmes comme grep, sed, cat et Cie, il utilise dpkg et dpkg-query afin de récupérer les informations nécessaires.

Côté performances, le temps de traitement est relativement long, principalement à cause des appels répétés aux outils Debian. En revanche, la réalisation d'un tel script est nettement plus aisée que la création d'un programme équivalent en C ou autre, ce qui justifie l'utilisation de la programmation Shell.

A noter, pour ceux qui souhaitent éviter le copier-coller, qu'un fichier exécutable est disponible ici. Une fois récupéré, pensez à taper :

$ chmod u+x deb-dep.sh

Présentation sommaire du script


Comme il le dit lui-même (deb-dep.sh --usage), le petit script accepte quelques arguments lors de son appel :

  • -s : passe en mode silencieux ; seule la liste finale des paquets supprimables est affichées.
  • -f : établit une distinction entre les différents numéros de version des paquets ; usage dangereux (cf. plus bas).
  • -r : filtre la liste finale avec celle contenu dans un fichier '~/.debpkg', afin de consever certains paquets.
  • --usage ou --help : affiche un message d'aide à l'utilisation.

Le script travaille sur quatre fichiers temporaires :

  • tmp_lst : liste de tous les paquets présents sur le système.
  • tmp_lst2 : liste évolutive contenant les paquets supprimables.
  • tmp_comp1 : fichier contenant deux numéros de version à comparer.
  • tmp_comp2 : fichier contenant deux numéros de version triés.

Ces fichiers sont bien sûr effacés après usage.

Quelques mots sur le code


Les commandes décrites ici ne seront pas détaillées (RTFM). Concernant le code Csh :

foreach element ($liste)
    ...
end

Cette boucle décompose le contenu de la variable $liste en mots. A chaque itération, un mot est mis dans la variable $element. La boucle se termine quand tous les mots ont été traités. Par ailleurs, '$liste' peut être remplacée par une commande Shell. Par exemple, pour parcourir le fichier tmp : foreach element (`cat tmp`).

cat tmp_lst2 | grep -v $target > tmp_lst2b
mv tmp_lst2b tmp_lst2

Description de la procédure : on ouvre le contenu du fichier tmp_lst2 (cat) et on envoie son contenu (|) vers grep, qui va laisser passer toutes les lignes ne contenant pas $target. Les lignes ayant passé les mailles du filet servent à créer (>) le fichier temporaire tmp_list2b. On renomme (mv) ensuite ce dernier en tmp_lst2.

Ici, on est obligé de passer par un second fichier. En effet, mettre '> tmp_lst2' reviendrait à écraser le fichier que l'on est en train de lire, ce qui ne passerait pas sans perte de données.

set nb = 1
...
@ nb++

Le '@' permet d'indiquer à l'interpréteur que le contenu de la variable est à manipuler en tant que forme numérique, et non chaîne de caractères. Ici, nb est un compteur indiquant le numéro du paquet traité.

set dep_list = `dpkg-query -p $pkg >& /dev/null`

if ($status == 0) then

    set dep_list = `dpkg-query -p $pkg | grep ...

    ...

Il arrive parfois qu'un paquet installé n'est aucune description (!?!) ; le cas s'est produit avec alsaplayer-als. Dans ce cas de figure, >& /dev/null évacue tous les messages (erreurs et autres). C'est là qu'intervient la variable $status, qui contient le code de sortie du dernier programme exécuté. Ainsi, on ne continue ici que si l'appel à dpkg-query s'est effectué correctement ($status = = 0).

Afin de diminuer le temps de traitement, on aurait pu eviter le second appel à dpkg-query avec :
dpkg-query -p $pkg > fichier >& /dev/null (cf. man csh)

Dans la pratique, l'interpréteur indique que la sortie de la commande est ambigüe... -> solution à la trappe !

sort tmp_comp1 > tmp_comp2

Un petit mot sur la méthode de comparaison des numéros de version : même s'ils sont très reglementés, les formats présents sont vraiment très hétéroclites (dpkg -l pour vous en convaincre !). Aussi, le programme sort est quelque fois dépassé. Un bel exemple est la comparaison entre '2.3.2.ds1-11' et '2.3.2.ds1-4' : '1' étant inférieur à '4', '2.3.2.ds1-4' est une version plus récente que '2.3.2.ds1-11' !

C'est pourquoi l'utilisation des numéros de version n'est ici que pour l'exemple, et que de telles comparaisons sont déconseillées du fait de leur manque de fiabilité.

Code source


#!/bin/csh


########################################################
## +++  LECTURE DES EVENTUELS PARAMETRES FOURNIS  +++ ##
########################################################


set fichier = '~/.debpkg'

# Par défaut
set bavard = 'oui'
set usage = 'non'
set vverif = 'non'
set filtre = 'oui'

# Lecture de la ligne de commande
foreach arg ($argv)
    switch ($arg)
        case '-s':
            set bavard = 'non'
            breaksw
        case '--usage':
        case '--help':
            set usage = 'oui'
            breaksw
        case '-f':
            set vverif = 'oui'
            breaksw
        case '-r':
            set filtre = 'non'
            breaksw
    endsw
end

# Affichage éventuel de la marche à suivre
if ($usage == 'oui') then

    echo "$0 -- Détermination des paquets non nécessaires"
    echo "Usage : $0 [-s] [-f] [--usage] [--help]"
    echo '  -s      : mode Silencieux'
    echo '  -f      : Force la vérification des numéros de version (déconseillé)'
    echo "  -r      : ne filtre pas le résultat final avec le fichier $fichier"
    echo '  --usage : affiche ce message'
    echo '  --help  : affiche ce message'

    exit 0

endif



##############################################
## +++  CONSTITUTION DE LA LISTE BRUTE  +++ ##
##############################################


# Récupération de la liste totale
dpkg -l | grep '^[A-Za-z]\{2\} ' > tmp_lst

# Mise sous forme 'nom_paquet@version'
cat tmp_lst | grep '^[A-Za-z]\{2\} ' | sed 's/.\{4\}\([^ ]*\) *\([^ ]*\).*/\1@\2/' > tmp_lst2
cp tmp_lst2 tmp_lst

if ($bavard == 'oui') then
    echo "Traitement de `wc -l tmp_lst | sed 's/\([0-9]*\).*/\1/' ` paquets..."
    set nb = 1
endif

# Pour chaque paquet installé
foreach line (`cat tmp_lst`)

    set pkg = `echo $line | sed 's/\([^@]*\).*/\1/'`

    if ($bavard == 'oui') then
        echo " [$nb] $pkg...  (`wc -l tmp_lst2 | sed 's/\([^ ]*\).*/\1/'`)"
        @ nb++
    endif

    # On récupère la liste...
    set dep_list = `dpkg-query -p $pkg >& /dev/null`

    if ($status == 0) then

        # ...sous forme 'pkg[@(>=|>>|...)@version] pkg[@(>=|>>|...)@version] ...'
        set dep_list = `dpkg-query -p $pkg | grep Depends | sed 's/Depends: \(.*\)$/\1/' | sed 's/\([^ ]*\) (\([^ ]*\) \([^ ]*\)),\? \?/\1@\2@\3 /g' | sed 's/,//g'`

        # Parcours de la liste des paquets requis
        foreach dep ($dep_list)

            set name = `echo $dep | sed 's/\([^@]*\).*/\1/'`
            set comp = `echo $dep | grep @ | sed 's/[^@]*@\([^@]*\).*/\1/'`
            set version = `echo $dep | grep @ | sed 's/[^@]*@[^@]*@\(.*\)/\1/'`

            # Parcours des paquets éjectables
            foreach target (`grep $name@ tmp_lst2`)

                if ($vverif == 'non') then

                    set suppr = 'oui'

                else

                    set tname = `echo $target | sed 's/\([^@]*\).*/\1/'`
                    set tversion = `echo $target | grep @ | sed 's/[^@]*@\([^@]*\)/\1/'`

                    set suppr = 'non'
                    echo $version > tmp_comp1
                    echo $tversion >> tmp_comp1

                    # Partie délicate en raison des nombreux formats de version
                    #  par ex, ne marche pas avec '2.3.2.ds1-11' et '2.3.2.ds1-4'
                    sort tmp_comp1 > tmp_comp2

                    set second = `tail -1 tmp_comp2`
                    if ($second == $version) then
                        set premier = $tversion
                    else
                        set premier = $version
                    endif

                    switch ($comp)
                        case '>=':
                            if ($premier == $version || $premier == $second) then
                                set suppr = 'oui'²
                            endif
                            breaksw
                        case '>>':
                            if ($premier == $version) then
                                set suppr = 'oui'
                            endif
                            breaksw
                        case '<=':
                            if ($premier == $tversion || $premier == $second) then
                                set suppr = 'oui'
                            endif
                            breaksw
                        case '\<\<':
                            if ($premier == $tversion) then
                                set suppr = 'oui'
                            endif
                            breaksw
                        default:
                            set suppr = 'oui'
                            breaksw
                    endsw

                endif

                if ($suppr == 'oui') then
                    cat tmp_lst2 | grep -v $target > tmp_lst2b
                    mv tmp_lst2b tmp_lst2
                endif

            end

        end

    endif

end



################################################
## +++  SUPPRESSION DES FICHIERS FAVORIS  +++ ##
################################################


# Supression des fichiers que l'on souhaite garder
if ($filtre == 'oui') then

    test -r $fichier

    # Si le fichier existe et est lisible
    if ($status == '0') then
        foreach element (`cat $fichier`)
            cat tmp_lst2 | grep -v $element > tmp_lst2b
            mv tmp_lst2b tmp_lst2
        end
    endif

endif

cat tmp_lst2 | sed 's/\([^@]*\).*/\1/'

rm -f tmp_lst
rm -f tmp_lst2
rm -f tmp_comp1
rm -f tmp_comp2

Il y a un commentaire sur cette page. [Afficher commentaires/formulaire]