2013-02-24 14 views
5

Ho due file con il numero N di colonneSubtructing n numero di colonne da due file con AWK

File1:

A 1 2 3 ....... Na1 
B 2 3 4 ....... Nb1 

File2:

A 2 2 4 ....... Na2 
B 1 3 4 ....... Nb2 

voglio un output in cui prima il valore della colonna da File1 verrà sottratto dalla prima colonna di File2 e in questo modo fino alla colonna N come mostrato di seguito:

A -1 0 -1 ........ (Na1-Na2) 
B 1 0  0 ........ (Nb1-Nb2) 

Come fare questo è AWK o script Perl in ambiente Linux?

risposta

1

Qualcosa di simile a questo:

use strict; 
use warnings; 

my (@fh, @v); 
for (@ARGV) { 
    open (my $handle, "<", $_) or die ("$!: $_"); 
    push @fh, $handle; 
} 
while (@v = map { [split ' ', <$_> ] } @fh and defined shift @{$v[0]}) { 
    print join(" ", (shift @{$v[1]}, map { $_ - shift(@{$v[1]}) } @{$v[0]})), "\n"; 
} 
close $_ for (@fh); 

Per eseguire:

perl script.pl input1 input2 
+1

mettere sempre '' $ nella stringa 'die' in modo da sapere il motivo per cui * * all'aperto fallito!. 'split ''' è quasi sempre quello che vuoi, piuttosto che 'split/\ s + /'. Il ciclo 'for' in stile C è in genere meglio scritto come elenco iteratore, qui' per il mio $ i (0 .. $ # v2) {...} ' – Borodin

1

Qualcosa di simile, forse? Temo di non poter testare questo codice perché al momento non ho un PC a portata di mano.

Questo programma prevede i nomi dei due file come parametri sulla riga di comando e restituisce i risultati a STDOUT.

use strict; 
use warnings; 
use autodie; 

my @fh; 
for my $filename (@ARGV) { 
    open my $fh, '<', $filename; 
    push @fh, $fh; 
} 

until (grep eof $_, @fh) { 
    my @records; 
    for my $fh (@fh) { 
    my $line = <$fh>; 
    chomp $line; 
    push @records, [ split ' ', $line ]; 
    } 

    $records[0][$_] -= $records[1][$_] for 1 .. $#{$records[0]}; 
    print "@{$records[0]}\n"; 
} 
+0

La virgola dopo' push' non ci appartiene: -) –

+0

@ChrisCharley: non so come sia entrato! Grazie. Fisso. – Borodin

2

A questo è già stata data risposta, ma aggiungerò un elemento di linea. Esso utilizza paste, per concatenare i file, e awk sottrarre:

paste file{1,2} | awk '{for (i=1;i<=NF/2;i++) printf "%s ", ($i==$i+0)?$i-$(i+NF/2):$i; print ""}' 

convalida:

$ cat file1 
A 1 2 3 4 5 
B 2 3 4 5 6 

$ cat file2 
A 2 2 4 10 12 
B 1 3 4 3 5 

$ paste file{1,2} | awk '{for (i=1;i<=NF/2;i++) printf "%s ", ($i==$i+0)?$i-$(i+NF/2):$i; print ""}' 
A -1 0 -1 -6 -7 
B 1 0 0 2 1 

Si richiede entrambi i file per avere lo stesso numero di colonne. Le colonne non numeriche dovrebbero trovarsi nella stessa posizione. Stampa il valore nel primo file se non numerico, altrimenti stampa la differenza.

+0

GRAZIE ......... anche AWK è utile –

2

Prova:

awk '{split($0,S); getline<f; for(i=2; i<=NF; i++) $i-=S[i]}1' OFS='\t' f=file1 file2 
2

Ecco un modo utilizzando GNU awk.Esegui come:

awk -f script.awk File2 File1 | rev | column -t | rev 

Contenuto del script.awk:

FNR==NR { 
    for(i=2;i<=NF;i++) { 
     a[$1][i]=$i 
    } 
    next 
} 

{ 
    for(j=2;j<=NF;j++) { 
     $j-=a[$1][j] 
    } 
}1 

In alternativa, ecco l'one-liner:

awk 'FNR==NR { for(i=2;i<=NF;i++) a[$1][i]=$i; next } { for(j=2;j<=NF;j++) $j-=a[$1][j] }1' File2 File1 | rev | column -t | rev 

Risultati:

A -1 0 -1 
B 1 0 0 
2
awk 'FNR==NR{for(i=2;i<=NF;i++)a[FNR"-"i]=$i;next}{printf "\n"$1" ";for(i=2;i<=NF;i++){printf $i-a[FNR"-"i]" "}}' file1 file2 
> cat file1 
A 1 2 3 
B 2 3 4 
> cat file2 
A 2 2 4 
B 1 3 4 
> awk 'FNR==NR{for(i=2;i<=NF;i++)a[FNR"-"i]=$i;next}{printf "\n"$1" ";for(i=2;i<=NF;i++){printf $i-a[FNR"-"i]" "}}' file1 file2 
A 1 0 1 
B -1 0 0 
> 

alternativa mettere questo in un file

#!/usr/bin/awk 
FNR==NR{ 
    for(i=2;i<=NF;i++) 
    a[FNR"-"i]=$i;next 
    } 
    { 
    printf "\n"$1" "; 
    for(i=2;i<=NF;i++) 
    { 
    printf $i-a[FNR"-"i]" " 
    } 
    } 

ed eseguire come:

awk -f file.awk file1 file2 
Problemi correlati