2013-01-25 18 views
11

Ho il seguente scriptStampa una virgola tranne l'ultima riga di Awk

awk '{printf "%s", $1"-"$2", "}' $a >> positions; 

dove $a memorizza il nome del file. In realtà sto scrivendo più valori di colonna in una riga. Tuttavia, vorrei stampare una virgola solo se non sono sull'ultima riga.

risposta

5

Lo farei trovando il numero di linee prima di eseguire lo script, ad es. con coreutils e bash:

awk -v nlines=$(wc -l < $a) '{printf "%s", $1"-"$2} NR != nlines { printf ", " }' $a >>positions 

Se il file ha solo 2 colonne, funziona anche la seguente alternativa coreutils. Dati Esempio:

paste <(seq 5) <(seq 5 -1 1) | tee testfile 

uscita:

1 5 
2 4 
3 3 
4 2 
5 1 

Ora sostituzione schede con a capo, paste assembla facilmente la data nel formato desiderato:

<testfile tr '\t' '\n' | paste -sd-, 

uscita:

1-5,2-4,3-3,4-2,5-1 
+0

Grazie, è tutto! – Perlnika

+0

FYI, questo uso di 'tr' e' paste' dovrebbe funzionare in qualsiasi ambiente POSIX, non è limitato ai coreutils GNU. – ghoti

1

Ecco un modo migliore, senza ricorrere a coreutils:

awk 'FNR==NR { c++; next } { ORS = (FNR==c ? "\n" : ", "); print $1, $2 }' OFS="-" file file 
+0

Perché è meglio di Thor? Quando avresti 'awk' ma non' wc'? –

+0

@ MichaelJ.Barber: Un'installazione interrotta è la risposta breve. Indipendentemente da ciò, è molto più imbarazzante scrivere semplicemente del codice che emula 'wc -l Steve

+1

Inoltre, Thor ha utilizzato due istruzioni di stampa quando solo una è realmente necessaria. Ha aggiunto una soluzione rapida, quando ciò che era necessario era una riscrittura. HTH. – Steve

19

approccio unico passaggio:

cat "$a" | # look, I can use this in a pipeline! 
    awk 'NR > 1 { printf(", ") } { printf("%s-%s", $1, $2) }' 

Si noti che ho anche semplificato la formattazione delle stringhe.

+1

Hey look, potresti anche usare 'awk <" $ a "'Nr ...'' e funzionerebbe in un unico processo (niente gatti)! –

+1

@ KrzysztofJabłoński Mi rendo conto che un odio fanatico per l'uso di 'cat' è di rigore qui, ma la tua alternativa manca completamente del punto. Altre risposte dipendevano da un approccio multi-pass che non consentiva l'utilizzo in una pipeline, che ho dimostrato essere possibile. –

+0

Sono d'accordo, che il punto previsto era quello di mostrare l'utilizzo in pipeline. Apparentemente non sono d'accordo sull'uso di 'cat' per quello scopo. Troppo spesso vedo quell'umile comando imbrigliato per iniziare un oleodotto. Per illustrare l'idea, preferirei mostrare qualcosa come 'dataGeneratorCmd | dataFilterCmd | awk '...' | terminalConsumerCmd'. Ma questa è una questione di gusti e preferenze. Ad ogni modo, non sentirti offeso. Questa è ancora una risposta abbastanza esaustiva. +1 –

0
awk '{a[NR]=$1"-"$2;next}END{for(i=1;i<NR;i++){print a[i]", " }}' $a > positions 
9

godere di questo:

awk '{printf t $1"-"$2} {t=", "}' $a >> positions 

Yeh, sembra un po 'complicato a prima vista. Così mi spiego, prima di tutto cerchiamo di cambiare printf su print per chiarezza:

awk '{print t $1"-"$2} {t=", "}' file 

e avere uno sguardo ciò che fa, ad esempio, per il file con questo semplice contenuto:

1 A 
2 B 
3 C 
4 D 

così esso produrrà la seguente:

1-A 
, 2-B 
, 3-C 
, 4-D 

il trucco è precedente t variabile che è vuota all'inizio. La variabile verrà impostata su {t=...} solo nel passaggio successivo dell'elaborazione dopo che è stato mostrato {print t ...}. Quindi se noi (awk) continuiamo ad iterare avremo la sequenza desiderata.

+0

La tua risposta è apparso nella coda di recensioni di bassa qualità. Modifica la tua risposta per includere una descrizione di come funziona. –

+0

Questa soluzione IMHO è migliore della risposta accettata perché il numero di linee a volte impossibile o difficile da calcolare prima di awk. Ad esempio, nel caso in cui voglio che wak esca se si incontra una linea vuota – leftjoin

+0

Questo è bello, ma se si stampano le righe in base a una condizione per saltare alcune linee di intestazione, non funzionerà poiché dopo aver saltato il primo, 't' assegnato. Funziona quindi usando la stessa condizione per '{t =", "}' ma sembra un po 'sporco per me. Es: '$ 1 ~/^ [0-9] * $/{stampa t $ 1} $ 1 ~/^ [0-9] * $/{t =", "}' non ci sono soluzioni più pulite? – r1verside

1

Si potrebbe pensare che ORS e OFS di awk sarebbe un modo ragionevole per gestire questa situazione:

$ awk '{print $1,$2}' OFS="-" ORS=", " input.txt 

Ma questo si traduce in un ORS finale perché l'ingresso contiene un ritorno a capo sull'ultima riga. Il newline è un separatore di record, quindi dalla prospettiva di awk c'è un ultimo record vuoto nell'input.È possibile aggirare questo problema con un po 'di hacker, ma la complessità risultante elimina l'eleganza del rivestimento unico.

Quindi ecco la mia opinione su questo. Dal momento che dici di "scrivere più valori di colonna", è possibile che il mucking con ORS e OFS possa causare problemi. Quindi possiamo ottenere l'output desiderato interamente con la formattazione.

$ cat input.txt 
3 2 
5 4 
1 8 
$ awk '{printf "%s%d-%d",t,$1,$2; t=", "} END{print ""}' input.txt 
3-2, 5-4, 1-8 

Questo è simile a Michael e gli approcci single-pass di Rook, ma utilizza un unico printf e correttamente utilizza la stringa di formato per la formattazione.

Probabilmente questo risultato è trascurabilmente migliore della soluzione di Michael perché un incarico dovrebbe richiedere meno CPU rispetto a un test e notevolmente migliore rispetto a qualsiasi soluzione multi-pass in quanto il file deve essere letto solo una volta.

Problemi correlati