2012-12-19 14 views
6

Ho il seguente codice per unire più file insieme. Funziona bene ma voglio sostituire i valori vuoti a 0, quindi ho usato -e "0". Ma non funziona. Qualche idea?bash unire più file con la sostituzione vuota (opzione -e)

for k in `ls file?` 
do 
    if [ -a final.results ] 
    then 
      join -a1 -a2 -e "0" final.results $k > tmp.res 
      mv tmp.res final.results 
    else 
      cp $k final.results 
    fi 

done 

esempio:

file1: 
a 1 
b 2 
file2: 
a 1 
c 2 
file3: 
b 1 
d 2 

Results: 
a 1 0 1 0 
b 2 1 0 
c 2 
d 2 

expected: 
a 1 1 0 
b 2 0 1 
c 0 2 0 
d 0 0 2 
+0

Non analizzare l'output di 'ls'; basta usare 'per k in file ?; do'. Inoltre, cita l'espansione di '$ k' per proteggere i caratteri speciali nel nome del file. – chepner

risposta

4

E 'scarsamente documentata, ma quando si utilizza l'opzione join-e funziona solo in combinazione con l'opzione -o. La stringa dell'ordine deve essere modificata ogni volta attorno al ciclo. Il seguente codice dovrebbe generare l'output desiderato.

i=3 
orderl='0,1.2' 
orderr=',2.2' 
for k in $(ls file?) 
do 
    if [ -a final.results ] 
    then 
      join -a1 -a2 -e "0" -o "$orderl$orderr" final.results $k > tmp.res 
      orderl="$orderl,1.$i" 
      i=$((i+1)) 
      mv tmp.res final.results 
    else 
      cp $k final.results 
    fi 
done 

Come potete vedere, inizia a diventare disordinato. Se è necessario estendere questo molto più lontano, potrebbe valere la pena di rimandare a uno strumento più potente come awk o python.

+1

Non è ancora perfettamente ragione ... Questa è l'uscita dello script: un 1 1 1 0 b 2 2 0 1 c 2 0 2 0 d 2 0 0 2 – Amir

+1

che è probabilmente perché hai un file final.results esistente. Prova a rimuoverlo per primo. La mia produzione è identica a quella che chiedi. – cmh

+0

Sì. Con il tuo esempio final.results, l'esecuzione di questo script dà 'a 1 1 1 0 b 2 2 0 1 c 2 0 2 0 d 2 0 0 2' come sopra.Chiaramente, è necessario rimuovere quel file prima di rieseguire. – cmh

0

ho rinunciato usando aderire e ha scritto il mio script in altro modo

keywords=`cat file? | awk '{print $1}' | sort | uniq | xargs` 
files=`ls file? | xargs` 
for p in $keywords 
do 
    x=`echo $p` 
    for k in $files 
    do 
    if grep -q ^$p $k 
    then 
     y=`cat $k | grep ^$p | awk '{print $2}'` 
     x=`echo $x $y` 
    else 
     echo $p $k 
     x=`echo $x 0`  
    fi 
    done 
    echo $x >> final.results 
done 
1

Supponendo non ci sono chiavi duplicate in un singolo file e le chiavi non contengono spazi, è possibile utilizzare gawk e un glob ordinato di File. Questo approccio sarebbe abbastanza veloce per file di grandi dimensioni e userebbe solo una quantità relativamente piccola di memoria rispetto a un glob di tutti i dati. Esegui come:

gawk -f script.awk $(ls -v file*) 

Contenuto del script.awk:

BEGINFILE { 
    c++ 
} 

z[$1] 

$1 in a { 

    a[$1]=a[$1] FS ($2 ? $2 : "0") 
    next 
} 

{ 
    for(i=1;i<=c;i++) { 
     r = (r ? r FS : "") \ 
     (i == c ? ($2 ? $2 : "0") : "0") 
    } 

    a[$1]=r; r="" 
    b[++n]=$1 
} 

ENDFILE { 

    for (j in a) { 
     if (!(j in z)) { 
      a[j]=a[j] FS "0" 
     } 
    } 

    delete z 
} 

END { 

    for (k=1;k<=n;k++) { 
     print b[k], a[b[k]] 
    } 
} 

ingresso di test/Risultati di grep . file*:

file1:a 1 
file1:x 
file1:b 2 
file2:a 1 
file2:c 2 
file2:g 
file3:b 1 
file3:d 2 
file5:m 6 
file5:a 4 
file6:x 
file6:m 7 
file7:x 9 
file7:c 8 

risultati:

a 1 1 0 4 0 0 
x 0 0 0 0 0 9 
b 2 0 1 0 0 0 
c 0 2 0 0 0 8 
g 0 0 0 0 0 0 
d 0 0 2 0 0 0 
m 0 0 0 6 7 0 
4

Per inciso, la versio GNU n di join supporta -o auto. Il numero -e e -o causano una frustrazione sufficiente a trasformare le persone in difficoltà di apprendimento. (Vedi anche How to get all fields in outer join with Unix join?). Come diceva cmh: non è documentato, ma quando si usa l'opzione -e funziona solo in combinazione con l'opzione -o.

soluzione generale:

cut -d ' ' -f1 file? | sort -u > tmp.index 
for k in file?; do join -a1 -e '0' -o '2.2' tmp.index $k > tmp.file.$k; done 
paste -d " " tmp.index tmp.file.* > final.results 
rm tmp* 

Bonus: come faccio a confrontare più filiali in git?

for k in pmt atc rush; do git ls-tree -r $k | cut -c13- > ~/tmp-branch-$k; done 
cut -f2 ~/tmp-branch-* | sort -u > ~/tmp-allfiles 
for k in pmt atc rush; do join -a1 -e '0' -t$'\t' -11 -22 -o '2.2' ~/tmp-allfiles ~/tmp-branch-$k > ~/tmp-sha-$k; done 
paste -d " " ~/tmp-allfiles ~/tmp-sha-* > final.results 
egrep -v '(.{40}).\1.\1' final.results # these files are not the same everywhere 
+0

Penso che il tuo primo punto sia meno di una parte e più della risposta giusta. Fornisce le opzioni per "unire" che hanno l'effetto desiderato. – WAF

+0

Guardando indietro, questo è giusto prima che il mio primo Git polpo si unisca. Abbiamo confrontato i tre rami fino a quando tutte le differenze erano pari a zero :-) –

Problemi correlati