#1 Le 19/03/2008, à 10:39
- Thibaud
[Bash]Comparer les lignes d'un carnet d'adresse
Bonjour !
Je débute avec bash.
Je dois comparer des lignes de carnets d'adresse avec des champs en colonne séparés par des tabulations, par exemple :
DUPONT Paris
DUPONT 75100 Paris
L'un des deux carnets d'adresse comporte des éléments supplémentaires. Dans mon exemple, c'est la première ligne qui peut être effacée. En revanche, dans l'exemple suivant :
DURAND 13007 Marseille
DURAND 06123456 Marseille
les deux lignes doivent être conservées.
Je peux bien sûr préparer les fichiers avec les commandes sort et uniq, mais ensuite ?
La commande diff permet de comparer les lignes d'un fichier, mais pas le contenu de ces lignes...
Une solution à laquelle j'ai pensé, c'est de faire une boucle qui lit, pour chaque ligne, chacun des champs. Si le champ existe dans la ligne suivante dans la même colonne, c'est que la ligne bouclée peut être supprimée. Sinon, on recommence cette fois avec la ligne précédente.
Par contre je ne me souviens plus comment on lit le champ d'une ligne à une position donnée, mais qui cherche trouve...
Mais, si vous avez plus simple, je suis preneur !
Merci de l'aide,
Thibaud.
Dernière modification par Thibaud (Le 19/03/2008, à 12:09)
Bienvenue sur mon site perso : http://thibaud.hulin.free.fr/dokuwiki
Pour l'utilisabilité et l'ergonomie des logiciels libres : http://ergolibre.tuxfamily.org
Apprentissage libre des langues : http://polyglotte.tuxfamily.org et http://www.akademia.ch/websites/ergolang/
Hors ligne
#2 Le 19/03/2008, à 11:08
- iuchiban
Re : [Bash]Comparer les lignes d'un carnet d'adresse
cut est ton ami.
ou awk aussi.
C'est depuis que Chuck Norris a laissé la vie sauve à un manchot que l'on dit que Linux est libre.
Chuck Norris n'a pas besoin d'éditer son premier message pour ajouter [Résolu]. Chuck Norris est toujours [Résolu], quoi qu'il arrive.
Hors ligne
#3 Le 19/03/2008, à 12:06
- Thibaud
Re : [Bash]Comparer les lignes d'un carnet d'adresse
Merci de ta réponse.
J'avais espéré faire ça avec join, qui fusionne les lignes avec un ou plusieurs champs identiques, mais je ne crois pas que ce soit possible.
cut devrait pouvoir extraire les champs, awk m'a l'air un peu plus compliqué à manier.
En premier lieu, j'essaie de lire la ligne précédente, l'actuelle et la suivante.
J'ai du faire une erreur de syntaxe (je débute), car je n'arrive pas à lire ces lignes dans ce script :
#!/bin/bash
## VARIABLES ##
prec="" # ligne précedente à comparer
act="" # ligne actuelle
suiv="" # ligne suivante
debut=0 # comptage de boucle
## PROGRAMME ##
echo Analyse du fichier $1 :
cat $1 | while read l
do
echo "ligne $debut:" $l
prec=$act
act=$suiv
suiv=$l
if [ $debut -lt 3 ] # on vérifie que l'on a au moins trois lignes
then
debut=$(($debut + 1))
else
for i in $l
do
echo "champ : "$i
done
fi
done
if [ $debut -lt 3 ]
then
echo "erreur, le fichier doit comporter au moins trois lignes"
echo debut = $debut
fi
echo prec : $prec
echo act : $act
echo suiv : $suiv
J'ai mis dans un fichier intitulé carnet ceci :
DUPONT 13100 MARSEILLE
DUPONT 0123456 MARSEILLE
DURAND
TINTIN
Et j'ai exécuté mon fichier comme suit : "script carnet"
Dernière modification par Thibaud (Le 19/03/2008, à 12:27)
Bienvenue sur mon site perso : http://thibaud.hulin.free.fr/dokuwiki
Pour l'utilisabilité et l'ergonomie des logiciels libres : http://ergolibre.tuxfamily.org
Apprentissage libre des langues : http://polyglotte.tuxfamily.org et http://www.akademia.ch/websites/ergolang/
Hors ligne
#4 Le 19/03/2008, à 18:51
- Thibaud
Re : [Bash]Comparer les lignes d'un carnet d'adresse
Bon, je bloque pour compter les champs d'un fichier séparés par des tabulations.
Par exemple avec ce fichier contact :
nom tel cp ville
DUPONT 13100 MARSEILLE
DUPONT 0123456 MARSEILLE
DURAND
TINTIN
Pour extraire les champs j'utilise le script suivant :
#!/bin/bash
cat $1 | while read ligne
do
i=0
for champ in $ligne
do
i=$(( $i + 1 ))
echo Champ n°$i : $champ
done
done
Problème : ce script retrouve 1 ou 3 champs par ligne, alors que les deux premières comprennent 4 champs séparés par des tabulations. Le champ vide est donc ignoré.
La commande cut -f $n permet bien d'extraire chaque champ y compris les champs vides mais il faut indiquer le numéro du champs. Il faudrait alors faire une boucle pour compter le nombre de champs, car sinon la boucle s'arrêtera à la fin du fichier et non pas de la ligne... là encore je ne sais pas comment faire.
Merci de l'aide,
Thibaud.
Bienvenue sur mon site perso : http://thibaud.hulin.free.fr/dokuwiki
Pour l'utilisabilité et l'ergonomie des logiciels libres : http://ergolibre.tuxfamily.org
Apprentissage libre des langues : http://polyglotte.tuxfamily.org et http://www.akademia.ch/websites/ergolang/
Hors ligne
#5 Le 20/03/2008, à 18:58
- Thibaud
Re : [Bash]Comparer les lignes d'un carnet d'adresse
Bon, je vous livre mon oeuvre.
Le problème qui subsiste, c'est d'indiquer de NE PAS fusionner certains champs ; donc qu'il existe des clés, par exemple la colonne "nom à afficher" dans thunderbird.
En effet, si j'ai mon fichier ressemble à ceci :
NOM Mail
Dupont
tintin@herge.com
Le script va donc mélanger fusionner ces deux lignes, alors qu'il s'agit de deux personnes différentes !
Si vous avez des idées...
Alors le voici :
#!/bin/bash
###
## Ce programme fusionne les champs d'un tableur au format csv
## lancer le programme comme suit :
## sh fusionne_champs.sh nom_du_fichier "caractère de séparation"
## le caractère peut être une tabulation : "\"
#######
# Paramètres :
fic=$1 # fic contenant les enregistrements
sep=$2 # separateur de ch_act
## VARIABLES ##
pre="" # ligne précedente à ch_prearer
act="" # ligne actuelle
suiv="" # ligne suivante
fusion="" # ligne fusionnée
diff=0
log="$(echo fusion\.$(date +%G%m%d%H%M)\.log)"
echo $log
# Initialise les fichiers
if [ -e new ] # fichier de sortie
then
rm new
fi
if [ -e tampon_pre ] # fichier de sortie
then
rm tampon_pre
fi
if [ -e tampon_act ] # fichier de sortie
then
rm tampon_act
fi
if [ -e $log ] # fichier de sortie
then
rm $log
fi
touch $log
## PROGRAMME ##
nb_enr=$(wc -l $fic|cut -d " " -f 1)
for ((enr=1 ; enr <= nb_enr ; enr++))
do
echo -e "$enr/$nb_enr"
# echo $enr.
pre="$act"
act=$(awk -F"$sep" 'NR == '$enr' {print $0}' $fic)
if [ -n "$pre" ]
then
echo "$pre">tampon_pre
echo "$act">tampon_act
# nb_act=$(awk -F "$sep" 'NR == '$enr' { print NF } ' $fic) #nb de ch_act dans la ligne act.
# nb_pre=$(awk -F "$sep" 'NR == '$(($enr - 1))' { print NF } ' $fic) #nb de ch_act dans la ligne préc. à ch_prearer
nb_act=$(awk -F "$sep" '{ print NF } ' tampon_act) #nb de ch_act dans la ligne act.
nb_pre=$(awk -F "$sep" '{ print NF } ' tampon_pre) #nb de ch_act dans la ligne préc. à ch_prearer
#echo -e "nb_act : $nb_act \nnb_pre : $nb_pre"
fusion=""
if [ "$nb_act" -gt "$nb_pre" ]
then
max=$nb_act
else
max=$nb_pre
fi
for ((c=1 ; c <= max ; c++))
do
if [ $c -gt $nb_pre ]
then
ch_pre=""
else
ch_pre=$(echo -e "$pre" |cut -f $c -d "$(echo -e "$sep")")
fi
if [ $c -gt $nb_act ]
then
ch_act=""
else
ch_act=$(echo -e "$act" |cut -f $c -d "$(echo -e "$sep")")
fi
# echo -e " $c/$max -Je compare $ch_act (act) avec $ch_pre (pre)"
if [ -n "$ch_act" ]
then
fusion=$(echo -e "$fusion$ch_act$sep")
else
fusion=$(echo -e "$fusion$ch_pre$sep")
fi
if [ -n "$ch_act" ]
then
if [ -n "$ch_pre" ]
then
c1=$(echo $ch_pre|tr -s a-z A-Z)
c2=$(echo $ch_act|tr -s a-z A-Z)
if [ "$c1" != "$c2" ]
then
diff=1 # les enregistrements sont différents
c=$max
fi
fi
fi
done
if [ $diff = 0 ]
then # enregistrements fusionnés
# $fusion=$(echo -e "$fusion" |sed -e "s/"$sep"$//g") # Pb non réglé de suppression de tabulation en fin de ligne
echo "$fusion" >> $log
act="$fusion"
else # enregistrements différents
echo -e "$pre" >> new
diff=0
# echo -e \=\> "$act est différent de $pre\n"
fi
fi
done
if [ $diff = 0 ] # s'il n'y a pas eu de fusion
then
echo $act >>new
# echo Ajout suite à fusion de : $act
fi
## RESULTATS ##
echo -e "\n"-----------------------------
echo $(wc -l new)
#cat new
echo $(wc -l $fic)
#cat f
echo $(wc -l $log)
#cat "$log"
Bienvenue sur mon site perso : http://thibaud.hulin.free.fr/dokuwiki
Pour l'utilisabilité et l'ergonomie des logiciels libres : http://ergolibre.tuxfamily.org
Apprentissage libre des langues : http://polyglotte.tuxfamily.org et http://www.akademia.ch/websites/ergolang/
Hors ligne
#6 Le 20/03/2008, à 23:56
- Saito
Re : [Bash]Comparer les lignes d'un carnet d'adresse
Salut,
je n'ai pas testé ton script, mais j'ai quelques petites améliorations/corrections à son égard:
#log="$(echo fusion\.$(date +%G%m%d%H%M)\.log)"
log=fusion.$(date +%G%m%d%H%M).log
#echo $(wc -l new)
wc -l new
Les echo ne servent à rien.
Question de goût, mais lorsque tu fais de petits tests tel que:
if [ -e tampon_act ] # fichier de sortie
then
rm tampon_act
fi
sache qu'il est possible de raccourcir de cette manière:
[ -e tampon_act ] && rm tampon_act
Mais je ne te le conseille que dans le cas de tests simples comme ceux que tu fais en début de script.
echo "$pre">tampon_pre
...
nb_pre=$(awk -F "$sep" '{ print NF } ' tampon_pre) #nb de ch_act dans la ligne préc. à ch_prearer
Tu peux remplacer ces deux lignes en une seule grâce à une redirection:
nb_pre=$(awk -F "$sep" '{ print NF } ' <<<"$pre")
et donc ne plus passer par une écriture sur le disque dans un fichier.
Aku-Soku-Zan_Shinsengumi
Hors ligne