Elaborazione del testo: unire ogni due righe con virgole


35

Ho più di 1000 righe in un file. Il file inizia come segue (numeri di riga aggiunti):

Station Name
Station Code
A N DEV NAGAR
ACND
ABHAIPUR
AHA
ABOHAR
ABS
ABU ROAD
ABR

Devo convertirlo in un file, con voci separate da virgola unendo ogni due righe. I dati finali dovrebbero apparire come

Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
...

Quello che stavo provando era: provare a scrivere uno script di shell e poi echocon una virgola in mezzo. Ma immagino che un one-liner efficace più semplice farebbe il lavoro qui potrebbe essere in sed/ awk.

Qualche idea?


@ l0b0 Hai modificato l'osservazione del PO secondo cui i numeri di riga sono "solo lì per una spiegazione" ...
jasonwryan,

@jasonwryan Siamo spiacenti, ho pensato che le linee fossero lì per una spiegazione. Errore di
analisi

Risposte:


39

Usa semplicemente cat(se ti piacciono i gatti ;-)) e paste:

cat file.in | paste -d, - - > file.out

Spiegazione: pastelegge da un numero di file e incolla le righe corrispondenti (riga 1 dal primo file con riga 1 dal secondo file ecc.):

paste file1 file2 ...

Invece di un nome di file, possiamo usare -(trattino). pasteprende la prima riga da file1 (che è stdin). Quindi, vuole leggere la prima riga da file2 (che è anche stdin). Tuttavia, poiché la prima riga di stdin era già stata letta ed elaborata, ciò che ora attende sul flusso di input è la seconda riga di stdin, che pastesi incolla felicemente sulla prima. L' -dopzione imposta il delimitatore come una virgola anziché una scheda.

In alternativa, fallo

cat file.in | sed "N;s/\n/,/" > file.out

PS Sì, si può semplificare quanto sopra

< file.in sed "N;s/\n/,/" > file.out

o

< file.in paste -d, - - > file.out

che ha il vantaggio di non usare cat.

Tuttavia, non ho usato questo idioma di proposito , per motivi di chiarezza - è meno prolisso e mi piace cat(I GATTI SONO PIACEVOLI). Quindi per favore non modificare.

In alternativa, se si preferisce incollare ai gatti (paste è il comando per concatenare i file in orizzontale, mentre cat li concatena in verticale), è possibile utilizzare:

paste file.in | paste -d, - -

Solo per menzionarlo di nuovo. I numeri di riga non fanno parte del file :)
mtk,

Il paste comando funziona perfettamente, puoi per favore dare qualche spiegazione in più a riguardo. I trattini ???
mtk,

2
I trattini significano "letti dallo stdin". Se si ripete la stessa sorgente di input, paste sa leggere da essa più volte per riga di output.
dubiousjim,

@sch: bella modifica, non la toccherò :-)
gennaio

1
Per quanto riguarda il tuo catargomento. Non sed "N;s/\n/,/" file.in > file.outfunziona?
Bernhard,

8

Nel caso in cui qualcuno atterrando qui stia cercando di combinare tutte le linee in una nave CSV, prova

cat file | tr '\n' ','

3
sed 'N;s/\n/,/' file

Usando sed, unisci (N) ogni 2 righe e sostituisci la nuova riga (\ n) con ",".


3
paste -sd ',\n' file.in > file.out

Inoltre, poiché stiamo semplicemente sostituendo un carattere con un altro (ogni altra riga con una virgola), possiamo lavorare sul file di input in atto:

paste -sd ',\n' file.in 1<> file.in

(ma attenzione, potrebbe non funzionare su sistemi non Unix con terminatori CRLF (come quelli Microsoft) che alcuni POSIX emulati pastepotrebbero trattare in modo non Unix)


Cosa ci 1fa qui qui 1<>? è un errore di battitura?
αғsнιη,

@ αғsнιη, guarda questo
iruvar

@iruvar grazie
αғsнιη

2

Ecco un one-liner (sebbene potenzialmente milioni di comandi-run-er) usando puro Bash:

(IFS=; while read -r name; do read -r code; printf '%s\n" "$name,$code"; done < file.in) > file.out

Uso una subshell (la paresi) in modo da non dover archiviare e ripristinare IFS. Quale altrimenti dovrebbe fare per non rovinare l'ambiente degli utenti nel caso in cui la fonte provenga. L'alternativa sarebbe Passare il nuovo IFS solo readcome in IFS= read -r name, IFS= read -r code.

Il fatto che tutti i comandi nel loop siano integrati nella shell rende le sue prestazioni accettabili ed è persino più veloce delle altre soluzioni per file di piccole dimensioni. Ma molte persone lo considererebbero una cattiva pratica e si dovrebbe fare attenzione quando si generalizza a qualsiasi altra cosa.


in generale yay per l'utilizzo di subshells per localizzare i cambiamenti dell'ambiente. Ma in questo caso non è necessario: puoi invece farlo while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, che è un linguaggio che vedo spesso negli script di shell. La -rbandiera readsignifica "interpretare il carattere '\' seguito dal carattere 'n' nel flusso stdin come due caratteri, piuttosto che come una nuova riga". Probabilmente, potrebbe essere più estetico creare la subshell come si fa che ripetere IFS='\n'.
dubiousjim,

@dubiousjim: tecnicamente -rmigliorata la soluzione. Grande! Non sono un fan dell'idea di passare IFSdue volte un cambio . Se avessi usato una lettura, super bella, ma non due volte. Ovviamente è una questione di opinione . L'uso di una subshell è un po 'superiore alla conoscenza generale di Bash, direi, quindi molte persone avranno difficoltà a comprenderne lo scopo. È una brutta cosa.
Eliminato il

2

Per il set completo di risposte, una possibile awksoluzione potrebbe essere:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*

@downvoter: cosa c'è di sbagliato nella mia risposta per meritare un downvote? Come può essere migliorato?
Bernhard,

Forse perché il pigro printf? Fallirà nel raro caso in cui il nome di una stazione contenga un identificatore di formato. (Vedi pastebin.com/wgxFttrJ per un esempio.) Ma questa è solo una supposizione, il voto negativo non è da parte mia.
arte

1

Hoary vecchia castagna di un awklinguaggio

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR

awk '{ORS=NR%2?",":"\n"};1'è più breve e più idioma
cuonglm

@cuonglm, ne dubito. In questo caso è ancora un liner nonostante printl'intento e sia chiaro. 1è altrettanto chiaro per i vecchi awkcome me, ma io preferiscoprint
iruvar,

Questa è stata la prima soluzione semplice che ho trovato facilmente configurabile su più di 2 righe. Ho combattuto sedper un po 'prima di cercare, ma ho awkreso più semplice la combinazione ogni 4 righe. Mi ha salvato un viaggio nel $EDITOR!
opello,

0

Possibile anche con Perl,

perl -pe 's/^\d+\.\s+//;$.&1?chomp:print","' file


0

Per esempio:

seq 0 70 | xargs -L 2 | sed 's/ /,/g'

Output: (nota: xargs -L number_of_columnsfunziona bene con quasi ogni numero di colonne, non solo ogni due righe)

0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
16,17
18,19
20,21
22,23
24,25
26,27
28,29
30,31
32,33
34,35
36,37
38,39
40,41
42,43
44,45
46,47
48,49
50,51
52,53
54,55
56,57
58,59
60,61
62,63
64,65
66,67
68,69
70

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.