# [script] gerer son fichier world avec eworld

## kaworu

Introduction

Salut !

depuis que je suis sous Gentoo j'aime bien avoir un contrôle sur mon fichier /var/lib/portage/world. Il y a plusieurs manipulation que je fais souvent, comme par exemple le trier et le copier pour regarder les paquets qu'il y a dedans, ou encore faire une copie de sauvegarde, ou simplement un petit grep <un_paquet> /var/lib/portage/world. Depuis quelques jour j'ai commencé à écrire un petit script pour faire tout ça (parce que je n'ai rien trouvé de tel dans les outils gentoo). Ce script s'apelle "naturelement" eworld, il est écris en bash et il est facilement extensible (il est simple de rajouter de nouvelles actions).

Utilisez eworld

Il suffit de copier le script dans un dossier de votre variable $PATH (comme par exemple /usr/local/bin) puis de le rendre exécutable grâce à la commande chmod.

```

# chmod +x /usr/local/bin/eworld

```

Maintenant vous pouvez lancer et utiliser ce script en tant q'utilisateur

```

$ eworld

```

Le lancer sans aucun argument reviens à afficher l'aide (eworld help).

Les actions disponibles

Pour toutes les actions il est maintenant possible de lancer eworld sans couleur. Décommentez la ligne "#NO_COLOR=1" dans le script, ou lancez eworld en exportant la variable $NO_COLOR avant :

```

$ export NO_COLOR=1

$ eworld

```

ou encore

```

$ NO_COLOR=1 eworld

```

eworld help : affiche chaques actions possible avec leur aide d'utilisation.

eworld copy : fait une copie triée du fichier /var/lib/portage/world. Si un argument est donné c'est ce fichier qui sera la copie, autrement le fichier ~/world est crée. /!\ si un fichier ~/world existe déjà il sera écrasé. 

exemple :

```

$ eworld copy /home/alex/doc/world_copie

* copy : /var/lib/portage/world to /home/alex/doc/world_copie : OK

$ ls /home/alex/doc/world_copie 

/home/alex/doc/world_copie

```

eworld backup : fais une sauvegarde du fichier /var/lib/portage/world actuel. La sauvegarde est faite dans le dossier  ~/.eworld/backup (il est possible bien que déconseillé de changer ce répértoire de sauvegarde en changeant la variable $SAVE_DIR dans le script) et le nouveau fichier s'appelle world___<date> où <date> est au format de la commande date "+%Y-%m-%d_%H:%M:%S". Si la variable $AUTO_CLEAN est "yes", l'action clean est automatiquement appelée par backup. Vous pouvez changez cette variable directement dans le script ou si c'est temporaire par un export de variable

```

$ export AUTO_CLEAN="false"

$ eworld backup

```

ou encore

```

$ AUTO_CLEAN="false" eworld backup

```

eworld clean : Nettoie le dossier ~/.eworld/backup. Laisse les 10 fichiers les plus récents et supprime les autres. /!\ Cette action se base sur le nom des fichiers (qui contient la date de leur copie) pour supprimer les plus anciens, donc ne modifier pas leur nom. clean est automatiquement appelée par backup selon la variable $AUTO_CLEAN cf plus haut). Le nombre de fichier à garder peut être changé directement dans le script ou par un export juste avant l'appel à eworld clean :

```

$ export SAVE_DIR_MAXF=0

$ eworld clean

```

ou encore

```

$ SAVE_DIR_MAXF=0 eworld clean

```

eworld update : Met à jours le fichier /var/lib/portage/world. Le fichier donné en argument est copié vers /var/lib/portage/world, si aucun fichier n'est donné en argument le fichier ~/world est pris s'il existe. Si l'utilisateur n'as pas le droit d'écrire dans /var/lib/portage cette action tente de passer par sudo, sinon il vous faudra lancer cette commande avec des droits plus élevés (root ou membre du groupe portage). Avant la mise à jours, l'action backup est appellé afin de faire une sauvegarde. Après la mise à jour la commande /usr/sbin/emaint --check world est appelé pour voir si le nouveau fichier /var/lib/portage/world est correct. Si emaint découvre un problème, update le signalera mais ne tentera pas de réparer le fichier world, c'est à vous de faire le boulot  :Wink:  Comme l'action backup est appelée automatiquement il

n'y pas trop de risque.

eworld grep : fais un grep sur le fichier /var/lib/portage/world. simple, utile et évite de tapper le chemin complet du fichier world  :Wink: 

exemple :

```

$ eworld grep 'kde-base/.*'

```

est pareil à

```

$ grep --color=auto 'kde-base/.*' /var/lib/portage/world

```

eworld checkdep : Vérifie pour chaque paquet dans le world s'il dépend d'un autre. Cette action est très interessante, car elle permet de "nettoyer" intelligement son fichier world.

Si par exemple vous avez installé dev-lang/php, dev-db/mysql et dev-db/phpmyadmin. Vous utilisez mysql pour autre chose, mais php ne vous est utile uniquement pour phpmyadmin. Dans ce cas, le plus logique est  d'avoir mysql et phpmyadmin dans votre world mais pas php. De cette manière le jour où vous décidez de vous séparer de phpmyadmin, le paquet php partira dans un emerge --depclean (si bien sûr, aucun autre programme ne dépend de lui), mais pas mysql (car il est dans votre world). De cette manière, si vous avez les trois dans votre world, eworld checkdep va vous signaler que php et mysql dépendent déjà de phpmyadmin, et vous pouvez vous poser la question "est ce que j'utilise php/mysql uniquement pour phpmyadmin ?". Si oui, vous pouvez retirer la ligne correspondante dans votre fichier world, ou sinon la laisser. Cela permet d'avoir un fichier world plus cohérent avec ce dont vous avez besoin, et permet de nettoyer beaucoup de paquets que l'on a emergé "vite

fais" qui font parti du système etc. Il y a actuellement deux implémentations de cette action. la première (celle par défaut) utilise le programme qdepends du paquet portage-utils et la deuxième utilise equery du paquet gentoolkit. la version avec qdepends est VRAIMENT plus rapide que equery (30secondes pour qdepends contre 15minutes avec equery) mais semble moins pertinente.

le script

```

#!/bin/bash

#

# eworld.sh

#

#

################################################################################

#       This program is free software; you can redistribute it and/or          #

#       modify it under the terms of the GNU General Public License            #

#       as published by the Free Software Foundation; either version 2         #

#       of the License, or (at your option) any later version.                 #

#                                                                              #

#       This program is distributed in the hope that it will be useful,        #

#       but WITHOUT ANY WARRANTY; without even the implied warranty of         #

#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          #

#       GNU General Public License for more details.                           #

#                                                                              #

#       You should have received a copy of the GNU General Public License      #

#       along with this program; if not, write to the Free Software            #

#       Foundation, Inc., 51 Franklin Street, Fifth Floor,                     #

#       Boston, MA  02110-1301, USA.                                           #

################################################################################

#

#

#

#-------------- VARIABLES -------------

# do not add variables unless you really

# think that it'll be useful for some 

# other action.

# variables must be in UPPER_CASE and

# should be declared like :

# YOUR_VAR=${YOUR_VAR:-"<default>"}

#--------------------------------------

#

# script version

VERSION="0.2.1"

# default world file path/name

WORLD_FILE=${WORLD_FILE:-"/var/lib/portage/world"}

# default copy world file

WORLD_FILE_COPY=${WORLD_FILE_COPY:-"$HOME/world"}

# save directory

SAVE_DIR=${SAVE_DIR:-"$HOME/.eworld"}

# max files in $SAVE_DIR

SAVE_DIR_MAXF=${SAVE_DIR_MAXF:-10}

# auto-clean option

AUTO_CLEAN=${AUTO_CLEAN:-"yes"}

# uncomment the next line if you don't want colored output

#NO_COLOR=1

#-------------- TOOLS -------------

# do not add tools functions unless

# you really think that it'll be

# useful for some other action.

# declare them like :

# my_function() { ...

# and not like :

# function my_function() {

#----------------------------------

#

# error output and exit

# you can use cecho syntax. please do mainingful errors

error() {

    cecho -red "* " -bold "ERROR: " "$@"

    exit 1

}

# colored echo

# ex : cecho -green "Gen" -blue "too" -red "is the best"

#      cecho -nocolor -blue

cecho() {

    while [ "$1" ]; do

        case "$1" in 

            -file)          color="\033[1m"  ;; # bold

            -dir)          color="\033[34;01m" ;; # blue

            -action)          color="\033[36;01m" ;; # cyan

            -cmd)         color="\033[32;01m" ;; # green

            -var)         color="\033[32;01m" ;; # green

            -args)        color="\033[33;01m" ;; # yellow

            -nc|-nocolor)   color="\033[00m" ;;

            -bold)          color="\033[1m"  ;;

            -black)         color="\033[30;01m" ;;

            -red)           color="\033[31;01m" ;;

            -green)         color="\033[32;01m" ;;

            -yellow)        color="\033[33;01m" ;;

            -blue)          color="\033[34;01m" ;;

            -magenta)       color="\033[35;01m" ;;

            -cyan)          color="\033[36;01m" ;;

            -white)         color="\033[37;01m" ;;

            -darkblack)     color="\033[30m" ;;

            -darkred)       color="\033[31m" ;;

            -darkgreen)     color="\033[32m" ;;

            -darkyellow)    color="\033[33m" ;;

            -darkblue)      color="\033[34m" ;;

            -darkmagenta)   color="\033[35m" ;;

            -darkcyan)      color="\033[36m" ;;

            -darkwhite)     color="\033[37m" ;;

            -n)             one_line=1;   shift ; continue ;;

            *)              echo -n "$1"; shift ; continue ;;

        esac

        shift

        [ ! "$NO_COLOR" ] && echo -en "$color"

        echo -en "$1"

        echo -en "\033[00m"

        shift

    done

    if [ ! $one_line ]; then

        echo

    fi

    unset one_line color

}

# test if a program is installed

need() {

    if which "$1" &>/dev/null; then

        return 0

    else

        prog=${2:-$1}

        error "this script need the \"$prog\" program. please run " -cmd "emerge --ask --verbose $prog"

    fi

}

# test if the world file is ok

emaint() {

    cecho -n -green "* " "Starting " -cmd "/usr/sbin/emaint --check world"

    emaint_error="$(/usr/sbin/emaint --check world | grep -v "Checking world for problems" | grep -v "Finished")"

    if [ "$emaint_error" ]; then

        echo

        error -cmd "emaint" " reported problems in your world file, please fix them."

    else

        cecho ": OK"

        return 0

    fi

}

#-------------- ACTIONS -------------

# add your function here. ALL new action

# function MUST begin like :

#

# function _<your_action_name>() {

# #help

#   if [ "$1" = "<your_action_name>" ]; then

#       cecho -action "<your_action_name> " ": description ..."

#       return 0

#    fi

# #/help

# ...

# <your code here>

# ...

# }

# 

# any new action function will be automatically detected

# and added to help action if declared properly.

#------------------------------------

#

function _help() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "help " ": display this help (you can use \"h\" or \"--help\" or \"-h\" also)"

        return 0

    fi

#/help

    cecho "$(basename $0) v$VERSION"

    cecho "usage : " -cmd "$(basename $0) " -action "<action> " -args "<arg>\n"

    cecho "actions :"

    for action in $ACTIONS; do

        echo -n "  "

        $action "help"

    done

    exit 0

}

function _copy() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "copy " ": copy (and sort) " -file "$WORLD_FILE " "to " -args "<arg> " "(or " -file "$HOME/world " "if no arguments)"

        return 0

    fi

#/help

    dest=${1:-"$WORLD_FILE_COPY"}

    if sort "$WORLD_FILE" > "$dest"; then

        cecho -green "* " -action "copy " ": " -file "$WORLD_FILE " "to " -file "$dest " ": OK"

    else

        error -action "copy " ": " -file "$WORLD_FILE " "to " -file "$dest " ": Failed"

    fi

}

function _backup() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "backup " ": save the current " -file "$WORLD_FILE " "to " -dir "$SAVE_DIR" ". use " -action "clean " "if " -var "\$AUTO_CLEAN " "is set to \"yes\" (current value is $AUTO_CLEAN)"

        return 0

    fi

#/help

    if [ ! -e "$SAVE_DIR/backup" ]; then

        mkdir "$SAVE_DIR/backup"

    fi

    now=$(date "+%Y-%m-%d_%H:%M:%S")

    backup_file="${SAVE_DIR}/backup/$(basename ${WORLD_FILE})___${now}"

    eworld_action copy $backup_file

    if [ "$AUTO_CLEAN" = "yes" ]; then

        eworld_action clean

    fi

    cecho -green "* " -action "backup " ": OK"

}

function _clean() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "clean " ": clean the " -dir "$SAVE_DIR " "directory. Remove all files in this directory but the " -var "\$SAVE_DIR_MAXF " "latest (current value is $SAVE_DIR_MAXF)"

        return 0

    fi

#/help

    nb_files=$(ls -1 "$SAVE_DIR/backup" | wc -l)

    if [ $nb_files -gt $SAVE_DIR_MAXF ]; then

        let "nb_files_to_rm = nb_files - SAVE_DIR_MAXF"

        files_to_rm="$(ls -1 "$SAVE_DIR/backup" | head -n $nb_files_to_rm)"

        for _file in $files_to_rm; do

            rm -f "${SAVE_DIR}/backup/${_file}"

        done

        cecho -green "* " -action "clean " ": $nb_files_to_rm files removed"

    else

        cecho -green "* " -action "clean " ": Nothing need to be cleanned ;)"

    fi

}

function _update() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "update " ": copy " -args "<arg> " "to " -file "$WORLD_FILE " "(" -file "$WORLD_FILE_COPY " "is used if no argument). use sudo if you're not allowed to write in " -dir "$(dirname $WORLD_FILE)" ". use " -action "backup" ". use " -cmd "/usr/sbin/emaint " "to check " -file "$WORLD_FILE " "after update."

        return 0

    fi

#/help

    if [ -w "$(dirname $WORLD_FILE)" ]; then

        copy_command="cp"

    else

        need sudo

        cecho -yellow "* " "using sudo"

        copy_command="sudo cp"

    fi

    src_file=${1:-"$WORLD_FILE_COPY"}

    if [ ! -f "$src_file" ]; then

        error "No " -file "$src_file " "to copy"

    fi

    eworld_action backup

    if $copy_command "$src_file" "$WORLD_FILE"; then

        cecho -green "* " -action "update " ": $copy_command " -file "$src_file " -file "$WORLD_FILE " ": OK"

    else

        error -action "update " ": $copy_command " -file "$src_file " -file "$WORLD_FILE " ": Failed"

    fi

    emaint

}

function _grep() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "grep " ": grep " -args "<arg> " -file "$WORLD_FILE "

        return 0

    fi

#/help

    cecho -green "* " -action "grep" " : grep \"$@\" " -file "$WORLD_FILE"

    if [ ! "$NO_COLOR" ]; then

        grep "$@" "$WORLD_FILE" --color=auto

    else

        grep "$@" "$WORLD_FILE"

    fi

}

function _checkdep() {

#help

    if [ "$1" = "help" ]; then

        cecho -action "checkdep " ": for each pkg in " -file "$WORLD_FILE " "check if it depends on another pkg. if " -args "<arg> " "is " -args "e " "or " -args "equery " "this action use " -cmd "equery " "from " -cmd "gentoolkit " "otherwise it use " -cmd "q" " from " -cmd "portage-utils" ". " -cmd "q " "is used by default because it's faster (but maybe less reliable)."

        return 0

    fi

#/help

    emaint

    if [ "$1" = "equery" ] || [ "$1" = "e" ]; then

        need "equery" "gentoolkit"

    else

        need "qdepends" "portage-utils"

    fi

    for pkg in $(cat $WORLD_FILE | sort); do

        if [ "$1" = "equery" ] || [ "$1" = "e" ]; then

            depends="$(equery depends "$pkg" | grep -v 'Searching' | grep '^\w\+')"

        else

            # FIXME : maybe not the perfect regexp

            depends="$(qdepends --query $pkg --verbose --nocolor | grep -v '^'$pkg'*' | sed 's/^\([^ ]*\):.* \([^ ]*'`basename $pkg`'[^ ]*\).*/\1  (\2)/')"

        fi

        if [ "$depends" ]; then

           cecho -file "$pkg" " :"

           cecho "$depends"

        fi

    done

}

#-------------- TESTS -------------

# do not add test unless you really

# think that it'll be useful for some 

# other action.

# please just add a little comment before

# the test.

#----------------------------------

#

# $WORLD_FILE must exists

if [ ! -f "$WORLD_FILE" ]; then

    error "file not found : " -file "$WORLD_FILE"

fi

# creat the $SAVE_DIR if not present

if [ ! -e "$SAVE_DIR" ]; then

mkdir "$SAVE_DIR/backup" || error "Failed while creating " -dir "$SAVE_DIR"

fi

# actions auto-detection.

ACTIONS=$(grep '^function \+_\w\+ *() *{ *$' "$0" | sed -e 's/function \(.*\)() *{/\1/')

# ------------- MAIN -------------

# lauch the appropriate action.

# if no args action help is called.

#---------------------------------

#

# action caller method

eworld_action() {

    for action in $ACTIONS; do

        if [ "_$1" = "$action" ]; then

            shift

            $action "$@"

            flag="ok"

        fi

    done

    if [ "$flag" != "ok" ]; then

        error -action "$1 " "is not a valid action, please run " -cmd "$0 help"

    fi

}

if [ $# -lt 1 ]; then 

    eworld_action "help"

fi

case "$1" in

    h|--help|-h)    eworld_action "help"    ;;

    *)              eworld_action "$@"      ;;

esac

```

Comment aider ?

Simplement en utilisant le script. Vous pouvez proposer de nouvelles action (voir plus bas) facilement, faire des critiques conseils etc. Voici ma TODOlist pour ceux qui veulent m'aider :

1) tester eworld checkdep avec equery et qdepends et me dire les différences de temps et de output. Chez moi equery met beaucoup de temps (15-18 minutes) mais chez d'autres c'est plutôt dans les 3 minutes (un problème dans mon cache portage ???). qdepends lui est très rapide (30 secondes) mais moins "bavard" que equery qui me trouve plus de dépendances.

2) vérifier le script, particulièrement les deux expressions régulières (ligne 282 et 312). la regexp ligne 282 parse la sortie de qdepends pour qu'elle ressemble à celle d'equery. celle 312 cherche les actions possibles dans le script.

3) expliquer comment ajouter une nouvelle action. Docummenter les variables / fonctions outils à disposition.

Comment ajouter une action

TODO  :Wink: 

EDIT

2007-01-24

  - v0.2.1

  - fix du commentaire pour l'ajout d'actions

  - checkdep démarre /usr/sbin/emaint --check world avant de commencer, pour s'assurer que le fichier world est correctement formé.

- Comme plusieurs action (checkdep et update et peut-être encore de futurs actions) utilisent emaint, une nouvelle fonction outil à été ajoutée (emaint).

- Ajout de la variable $NO_COLOR. Il est possible maintenant de démarrer le script en exportant cette variable (ou en l'initialisant dans le code) pour éviter un affichage coloré. Si vous ajouter une action qui produit un affichage coloré SANS passer par la fonction outils cecho n'oubliez pas de prendre cette variable en compte (comme c'est le cas pour l'action grep).

----------

## truc

sans vouloir discrediter ton travail, tu dis ne pas avoir trouver d'outil gerant ton ce fichier entre autre, as tu jeter un oeil à udept:

```
app-portage/udept

    gentoo:                0.5.96.2 0.5.99.0.2.95 0.5.99.0.2.95-r1* {:0} 

    Homepage:              http://catmur.co.uk/gentoo/udept

    Description:           A Portage analysis toolkit

    License:               ( GPL-2 ) 

    Use flags:             bash-completion

```

il a d'ailleurs tout un thread qui lui est consacré: Clean out your world file

----------

## kaworu

Salut !

en faite j'ai testé udept et visiblement il n'as pas les même objectif que mon petit script.

udept intègre des fonctionnalités pour portage de manière générale (des fonctions que l'on retrouve dans equery ou q), des algo complexes, une syntaxe peu lisible (ça c'est mon avis  :Wink:  ).

Mon script est juste un petit outil qui permet de gagner du temps, sans algo compliqué (parce que d'autres plus capables que moi l'on déjà fait  :Wink:  ). Il faut qu'il reste simple.

Si j'ose la comparaison, udept me fais penser a equery (actions complexes comme les calculs de dépendances inverses), et eworld me fais plus penser à eselect, un outil pratique, pas indispensable mais drôlement utile  :Wink: 

----------

