Newsgroup: it.comp.os.linux.software
Titolo: Ancora aiuto... combinare 2 liste!

Ciao,
Sto facendo uno script che periodicamente scarica un file con una lista
di indirizzi e-mail.
Il file scaricato ha questa forma:
$ cat file1
pi...@topolinia.it
pl...@tololinia.it
quiq...@paperopoli.it
pape...@paperopoli.it
topo...@topolinia.it
...
Mi serve confrontarlo con un file che in prima colonna ha gli indirizzi
e-mail e in seconda colonna ha in nomi dei proprietari degli indirizzi.
Le due colonne sono separate da una tabulazione, così:
$ cat file2
pi...@topolinia.it
	pippo
pl...@tololinia.it
	pluto
pape...@paperopoli.it
	paperino
...
E creare un nuovo file ottenuto da file2 appendendo le righe contenenti
gli indirizzi in più presenti in file1. Questo vuol dire che il file
finale dovrà essere tipo questo:
$ cat file3
pi...@topolinia.it
	pippo
pl...@tololinia.it
	pluto
quiq...@paperopoli.it
pape...@paperopoli.it
	paperino
topo...@topolinia.it
...
Ho googlato e sperimentato parecchio, provando fgrep, paste e cose
simili, ma non ho trovato una soluzione finora.
Che ne dite? come posso fare?
L'ordine delle righe è ininfluente.
grazie
ciao
gerlos
-- 
"Solo lo scienziato è vero poeta: ci dà la luna, ci promette le stelle,
 ci farà un nuovo universo se sarà il caso."
   _                                  Allen Ginsberg
  (y)        < 
http://gerlos.altervista.org
 >
   =   gerlos  + - - >  gnu/linux registred user #311588
        Per scrivermi togli i trattini e i baffi!

 Che ne dite? come posso fare?
sed e' tuo amico.
-- 
Best Regards, Jack
Linux User #264449
Powered by Debian GNU/Linux on AMD64

 
Lo so, e anche awk è un amico, il fatto è che non ci ho tanta
confidenza, e mi serve un piccolo suggerimento su come rompere il
ghiaccio...
grazie


 L'ordine delle righe è ininfluente.
Se non vuoi scomodare comandi "complicati", potresti fare così:
1) copi file2 su file3 (occhio che così cancelli quello che sta già
in file3):
cat file2 > file3
2) crei il file di appoggio file4, in cui copi solo la prima colonna
di file2 (quella degli indirizzi), levando quindi i nomi:
cut -f1 file2 > file4
3) Cerchi con grep ogni non-occorrenza di file4 in file1, e la accodi
al file3:
grep -vif file4 file1 >> file3
4) cancelli file4
rm file4
Tutto in una volta, velocizzando:
cat file2 > file3 && cut -f1 file2 > file4 && grep -vif file4 file1 >> file3 && rm file4
-- 
Bye, Lem
Ceterum censeo ISLAM esse delendum
__________________________________
Combatti l'AIDS col tuo computer: 
http://fightaidsathome.scripps.edu/

 L'ordine delle righe è ininfluente.
Ho trovato come fare:
 awk '{ print $1 }' file1 |  grep -vif - file2 >> file1
Mi sono reso conto che in effetti mi serve di più aggiornare il
contenuto di file1 che creare un nuovo file.
Si sarebbe potuto fare anche con il solo awk, senza ricorrere a grep?
Non lo so, a un certo punto mi sono perso nella documentazione di awk...
grazie a tutti


 writes:

io creerei file3 mettendo assieme file1 e file2 con cat, e pipando
quello che viene fuori dal cat attraverso sort e uniq.  in questo modo
file3 contiene una linea per ognuna delle linee viste o in file1, o in
file2, o in entrambi:
cat file1 file2 | sort | uniq > file3   # UNTESTED
-- 
|   Maurizio Loreti -- 
ROT13:ybe...@cq.vasa.vg
   |  (@_
| Un. of Padova, Dept. of Physics, Padova, Italy |  //\
|     
http://www.pd.infn.it/~loreti/mlo.html
     |  V_/_

 cat file1 file2 | sort | uniq > file3   # UNTESTED
Però così, per esempio, in file3 stanno entrambe le righe
pl...@topolinia.it
	pluto
e
pl...@topolinia.it
che condividono il medesimo indirizzo, e mi pare che non fosse
voluto.


 Si sarebbe potuto fare anche con il solo awk, senza ricorrere a grep?
Oh, son certo di sì. ;)


 voluto.
pensavo che le righe dovessero essere dello stesso tipo in entrambi i
files (del resto, le scrive lui); ma se non e' cosi' hai ragione, il
mio metodo non funziona.


grep -F -f <(cut -f1 file2) file1 > $TMPFILE
cat $TMPFILE >> file2

 cat file1 file2 | sort | uniq > file3   # UNTESTED
sort file1 file2 | uniq > $TMPFILE
va decisamente meglio, stando attenti a generare
*CORRETTAMENTE* il file temporaneo.
L'idea sarebbe buona ma bisognerebbe dire a uniq 
di trascurare i campi che non interessano.
Purtroppo uniq ha una opzione per trascurare *i*
*primi* *campi* e qui serve una opzione per 
confrontare *solo* il primo campo.
Vicolo cieco, a meno che qualcuno conosca una 
versione di uniq che lo permetta.

 grep -vif file1 file2 >> file1
Purtroppo non funge. Questo grep i cerca le righe di
file2 che non hanno corrispondenti in file1, esattamente 
il contrario delle specifiche.
Ma il problema serio e` l'uso leggero della redirezione;
usi lo stesso file in input ed output. QUESTO E` MALE
usa un file temporaneo od una pipe.

 versione di uniq che lo permetta.
aggiungere cut alla pipe ?

 come rompere il ghiaccio...
AWK e` un linguaggio di programmazione _simile_ al C
Puoi usare cicli while, for, if then else, confrontare
stringhe, sommare numeri et cetera.
http://www.gnu.org/manual/gawk/

 aggiungere cut alla pipe ?
No, perche` taglierebbe il secondo campo.

 file2 che non hanno corrispondenti in file1
e le aggiunge a file1. Esattamente quello che ha detto di voler fare,
in seconda battuta.


 usa un file temporaneo od una pipe.
Nel caso specifico la cosa non crea problemi, a parte l'inutile
recheck di quanto appena aggiunto.
Il vero problema è che, aggiornando così il file1, l'OP si troverà in
file1 anche dei nomi. Se ripetessse l'operazione una seconda volta,
avrebbe dei match con file2 che produrrebbero doppioni in file1.


 usa un file temporaneo od una pipe.
Nel caso specifico la cosa non crea problemi, a parte l'inutile
recheck di quanto appena aggiunto.


 Nel caso specifico la cosa non crea problemi,
Avere lo stesso file aperto due volte in lettura 
ed in scrittura non crea problemi di inconstistenza 
dei dati?

 ...
Il comando di Lem Novantotto darebbe:
pi...@topolinia.it
pl...@tololinia.it
quiq...@paperopoli.it
pape...@paperopoli.it
  paperino
topo...@topolinia.it
Allora, risponde alle specifiche?

 ...
Il comando di Lem Novantotto darebbe:
pi...@topolinia.it
pl...@tololinia.it
quiq...@paperopoli.it
pape...@paperopoli.it
topo...@topolinia.it
Allora, risponde alle specifiche?

 Allora, risponde alle specifiche?
grep -vif file1 file2 >> file1
fa esattamente la stessa cosa del comando
awk '{ print $1 }' file1 |  grep -vif - file2 >> file1
postato dall'OP come soluzione soddisfacente al suo problema. Solo,
lo fa in modo più semplice e diretto, no? Io stesso ho evidenziato
che tale soluzione non rispecchia le specifiche originali, ma se va
bene a lui...
Se vuoi una diversa soluzione, che soddisfi le specifiche
*originali*, allora considera la prima che ho postato:
cat file2 > file3 && cut -f1 file2 > file4 && grep -vif file4 file1 >> file3 && rm file4
che si può pure sintetizzare con:
cat file2 > file3 && cut -f1 file2 | grep -vif - file1 >> file3


 dei dati?
In generale il problema c'è eccome, direi... ma il caso specifico mi
pare esente.


 file3
Meglio sarebbe:
cp file2 file3 &&....
Molto piu' veloce di cat. Pero` il file temporaneo 
file3 dovrebbe essere generato con mktemp.
cut -f1 file2 | grep -vif - ...
e` (quasi) esattamete equivalente a:
grep -vif <(cut -f1 file2) ...
Pero` questa sintassi e` solo per la bash.

 L'ordine delle righe è ininfluente.
non ho capito se alla fine hai trovato una soluzione che soddisfacesse
tutte le specifiche.
in caso contrario i consiglio uno script python, è sicuramente piu'
adatto a questo tipo di manipolazione dei file.

 Pero` questa sintassi e` solo per la bash.
Ah, ma io non mi permettevo mica di mettere in dubbio la bontà della
tua soluzione. Anzi: anche se non ne so molto, ho già avuto modo di
accorgermi, seguendo il gruppo, che padroneggi benissimo la bash. :)


 
E' un po' illeggibile, ma fa quello che serve :
michele:/tmp$ cat f_combine.py
#!/usr/bin/env python
import string
d_f1 = dict( (i.strip(), "") for i in open("./file1").readlines() )
d_f1.update( dict( map(string.strip, i.split("\t")) for i in 
open("./file2").readlines()  ) )
f = open("./file3", "w")
[ f.write("%s\t%s\n" % (k, v) ) for (k,v) in d_f1.iteritems() ]
michele:/tmp$ python f_combine.py && cat file3
quiq...@paperopoli.it
topo...@topolinia.it
pape...@paperopoli.it
  paperino
pl...@tololinia.it
      pluto
pi...@topolinia.it
      pippo
Michele

RedWiz ha scritto:

Guarda, io sono contento con la soluzione che ho trovato.
La soluzione proposta da Lem Novantotto
grep -vif file1 file2 >> file1
NON fa la stessa cosa di
awk '{ print $1 }' file1 | grep -vif - file2 >> file1
o di
cut -f1 file1 | grep -vif - file2 >> file1
perchè mi fa perdere la colonna con i nomi.
Immagino che non si offenderà nessuno se mi tengo la mia soluzione con
awk e grep, che sarà pure inelegante ma almeno è leggibile...


 perchè mi fa perdere la colonna con i nomi.
Tagliamo la testa al toro:
grep -vif file1 file2 >> file1
non può far perdere niente in file1: gli aggiunge righe, non gli
cancella nulla. E le righe che aggiunge, proveniendo da file2, hanno
tutte un indirizzo e un nome, e le aggiunge con indirizzo e nome.
... :((((
Mi accorgo ora, dopo aver sprecato fiumi di bit inutilmente, che il
tuo è stato solo un lapsus! Tu vuoi aggiornare *file2* (che ha i
nomi!), e non file1 come hai scritto: cosa molto più sensata,
infatti. E allora tutto cambia! La tua soluzione non è quella che hai
scritto, ma:
awk '{ print $1 }' file2 | grep -vif - file1 >> file2
che è equivalente a:
cut -f1 file2 | grep -vif - file1 >> file2
o a quanto ti ha suggerito mallin.shetland, per esempio.
