Pravděpodobně nejjednodušší je zkombinovat první tři pole s awk:
awk '{print $1 "_" $2 "_" $3 " " $4}' filename
Pak můžete použít join
normálně na "pole 1"
můžete zkusit toto
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
výstup
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
foo 1 boo 2.3
Pokud chcete vynechat neobvyklé řádky
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
výstup
$ ./shell.sh
foo 1 scaf 3 4.5
bar 2 scaf 3.3 1.00
Zde je správné odpověď (z hlediska použití standardních GNU coreutils a nikoli psaní vlastního skriptu v perl/awk jak si to vyjmenujete).
$ 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, jak to funguje:
-
Nejprve použijeme skvělý nástroj
join
který může sloučit dva řádky.join
má dva požadavky:- Můžeme se připojit pouze jedním polem.
- Oba soubory musí být seřazeny podle klíčového sloupce!
-
Potřebujeme vygenerovat klíče ve vstupních souborech a k tomu používáme jednoduchý
awk
skript:$ 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
Vidíte, přidali jsme 1. sloupec s nějakým klíčem jako „foo-1-scaf ".Totéž děláme s souborem2." .MIMOCHODEM.
<file awk
, je prostě skvělý způsob zápisuawk file
nebocat file | awk
.Měli bychom také třídit naše soubory klíčem, v našem případě je to sloupec 1, takže na konec příkazu přidáme
| sort -k1,1
(řadit podle textu od sloupce 1 do sloupce 1) -
V tomto okamžiku bychom mohli vygenerovat soubory file1.with.key a soubor2.s.klíčem a připojte se k nim, ale předpokládejme, že tyto soubory jsou obrovské, nechceme je kopírovat přes souborový systém. Místo toho můžeme použít něco, co se nazývá
bash
proces substituce pro generování výstupu do pojmenovaného kanálu (tím se zabrání zbytečnému vytváření mezilehlého souboru). Pro více informací si prosím přečtěte poskytnutý odkaz.Naše cílová syntaxe je:
join <( some command ) <(some other command)
-
Poslední věcí je vysvětlit efektní argumenty spojení:
-j1 -o1.2,1.3,1.4,1.5,2.5
-j1
- spojení klíčem v 1. sloupci (v obou souborech)-
-o
- vypisují pouze tato pole1.2
(1. pole souboru2),1.3
(1. soubor, sloupec 3) atd.Tímto způsobem jsme spojili řádky, ale
join
vypíše pouze nezbytné sloupce.
Poučení z tohoto příspěvku by mělo být:
- měli byste ovládat coreutils jsou tyto nástroje v kombinaci velmi výkonné a téměř nikdy nepotřebujete napsat vlastní program pro řešení takových případů,
- nástroje core utils jsou také bleskově rychlé a důkladně testované, takže jsou vždy nejlepší volbou.
Příkaz join se těžko používá a spojuje pouze jeden sloupec
Rozsáhlé experimentování a podrobné zkoumání manuálových stránek ukazuje, že nemůžete přímo spojit více sloupců – a všechny mé pracovní příklady spojení, kupodivu, používají pouze jeden spojovací sloupec.
V důsledku toho bude jakékoli řešení vyžadovat, aby sloupce, které mají být spojeny, byly nějakým způsobem zřetězeny do jednoho sloupce. Standardní příkaz join také vyžaduje, aby jeho vstupy byly ve správném seřazeném pořadí – v GNU joinu (info coreutils join) je poznámka o tom, že nemusí vždy vyžadovat seřazená data:
Nicméně, jako rozšíření GNU, pokud vstup nemá žádné nepárovatelné řádky, pořadí řazení může být jakékoli pořadí, které považuje dvě pole za rovná, pokud je výše popsané srovnání řazení považuje za stejné.
Jedním z možných způsobů, jak to provést s danými soubory, je:
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
Tím se na začátku vytvoří složené třídicí pole pomocí ':' k oddělení podpolí a pak se soubor seřadí - pro každý ze dvou souborů. Příkaz join poté spojí dvě složená pole, ale vytiskne pouze nesložená (nespojená) pole.
Výstup je:
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Neúspěšné pokusy o připojení dělají to, co neudělají
připojit -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 soubor1 soubor2
V systému MacOS X 10.6.3 to dává:
$ 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
$
Toto je spojení na poli 3 (pouze) - což není to, co se chce.
Musíte se ujistit, že vstupní soubory jsou ve správném pořadí.