GNU/Linux >> Znalost Linux >  >> Linux

Spojení více polí v textových souborech na Unixu

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:

  1. 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!
  2. 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ápisu awk file nebo cat 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)

  3. 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)

  4. 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 pole 1.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í.


Linux
  1. Importujte více souborů SQL do MySQL

  2. Dd:Více vstupních souborů?

  3. Jak rozdělit jeden textový soubor do více souborů *.txt?

  1. Rozdělte obrázek dd do více souborů

  2. Jak přejmenovat více souborů v jednom příkazu nebo skriptu v Unixu?

  3. Třídit textové soubory s více řádky jako řádek

  1. Jak spojit dva textové soubory v Linuxu

  2. Jak komprimovat více souborů v systému Linux

  3. Tail více vzdálených souborů