Probabilmente è più semplice combinare i primi tre campi con awk:
awk '{print $1 "_" $2 "_" $3 " " $4}' filename
Allora puoi usare join
normalmente sul "campo 1"
puoi provare questo
awk '{
o1=$1;o2=$2;o3=$3
$1=$2=$3="";gsub(" +","")
_[o1 FS o2 FS o3]=_[o1 FS o2 FS o3] FS $0
}
END{ for(i in _) print i,_[i] }' file1 file2
uscita
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
foo 1 boo 2.3
Se vuoi omettere righe non comuni
awk 'FNR==NR{
s=""
for(i=4;i<=NF;i++){ s=s FS $i }
_[$1$2$3] = s
next
}
{
printf $1 FS $2 FS $3 FS
for(o=4;o<NF;o++){
printf $i" "
}
printf $NF FS _[$1$2$3]"\n"
} ' file2 file1
uscita
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
Ecco la corretta risposta (in termini di utilizzo dello standard GNU coreutils tools e non scrivere script personalizzati in perl/awk lo chiami tu).
$ join -j1 -o1.2,1.3,1.4,1.5,2.5 <(<file1 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1) <(<file2 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1)
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
OK, come funziona:
-
Prima di tutto useremo un ottimo strumento
join
che può unire due righe.join
ha due requisiti:- Possiamo unirci solo da un singolo campo.
- Entrambi i file devono essere ordinati per colonna chiave!
-
Dobbiamo generare chiavi nei file di input e per questo usiamo un semplice
awk
script:$ cat file1 foo 1 scaf 3 bar 2 scaf 3.3 $ <file1 awk '{print $1"-"$2"-"$3" "$0}' foo-1-scaf foo 1 scaf 3 bar-2-scaf bar 2 scaf 3.3
Vedi, abbiamo aggiunto la prima colonna con una chiave come "foo-1-scaf ".Facciamo lo stesso con file2 .A proposito.
<file awk
, è solo un modo stravagante di scrivereawk file
ocat file | awk
.Dovremmo anche ordinare i nostri file per chiave, nel nostro caso questa è la colonna 1, quindi aggiungiamo alla fine del comando il
| sort -k1,1
(ordina per testo dalla colonna 1 alla colonna 1) -
A questo punto potremmo semplicemente generare i file file1.with.key e file2.con.chiave e unisciti a loro, ma supponiamo che quei file siano enormi, non vogliamo copiarli sul filesystem. Invece possiamo usare qualcosa chiamato
bash
sostituzione del processo per generare l'output in named pipe (questo eviterà la creazione di file intermedi non necessari). Per maggiori informazioni, leggi il link fornito.La nostra sintassi di destinazione è:
join <( some command ) <(some other command)
-
L'ultima cosa è spiegare gli argomenti di join fantasiosi:
-j1 -o1.2,1.3,1.4,1.5,2.5
-j1
- join per chiave nella prima colonna (in entrambi i file)-
-o
- restituisce solo quei campi1.2
(1° campo file2),1.3
(1a colonna 3 del file), ecc.In questo modo abbiamo unito le righe, ma
join
restituisce solo le colonne necessarie.
Le lezioni apprese da questo post dovrebbero essere:
- dovresti padroneggiare i coreutils pacchetto, questi strumenti sono molto potenti se combinati e quasi non ne hai mai bisogno scrivere un programma personalizzato per gestire questi casi,
- Gli strumenti di core utils sono anche velocissimi e ampiamente testati, quindi sono sempre la scelta migliore.
Il comando join è difficile da usare e si unisce solo su una colonna
Un'ampia sperimentazione e un attento esame delle pagine di manuale indicano che non è possibile unire direttamente più colonne e tutti i miei esempi funzionanti di unione, stranamente, utilizzano solo una colonna di unione.
Di conseguenza, qualsiasi soluzione richiederà che le colonne da unire siano concatenate in una colonna, in qualche modo. Il comando join standard richiede anche che i suoi input siano nell'ordine corretto - c'è un'osservazione nel join GNU (info coreutils join) che non richiede sempre dati ordinati:
Tuttavia, come estensione GNU, se l'input non ha righe non abbinabili, l'ordinamento può essere qualsiasi ordine che consideri due campi uguali se e solo se il confronto dell'ordinamento sopra descritto li considera uguali.
Un modo possibile per farlo con i file forniti è:
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file1 |
sort > sort1
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file2 |
sort > sort2
join -1 1 -2 1 -o 1.2,1.3,1.4,1.5,2.5 sort1 sort2
Questo crea un campo di ordinamento composito all'inizio, utilizzando ':' per separare i sottocampi, quindi ordina il file - per ciascuno dei due file. Il comando join quindi unisce i due campi compositi, ma stampa solo i campi non compositi (non join).
L'output è:
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Tentativi falliti di fare in modo che join faccia ciò che non farà
unisciti -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
Su MacOS X 10.6.3, questo dà:
$ cat file1
foo 1 scaf 3
bar 2 scaf 3.3
$ cat file2
foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
$ join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
foo 1 scaf 3 4.5
bar 2 scaf 3.3 4.5
$
Questo è entrare nel campo 3 (solo) - che non è ciò che si desidera.
Devi assicurarti che i file di input siano nell'ordine corretto.