Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#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