Processamento de texto - junte-se a cada duas linhas com vírgulas

35

Eu tenho mais de 1000 linhas em um arquivo. O arquivo inicia da seguinte forma (números de linha adicionados):

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

Eu preciso converter isso em um arquivo, com entradas separadas por vírgula, juntando-se a cada duas linhas. Os dados finais devem ter a aparência de

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

O que eu estava tentando era - tentando escrever um script de shell e depois echocom vírgula no meio. Mas eu acho que uma linha simples eficaz mais simples faria o trabalho aqui pode estar em sed/ awk.

Alguma ideia?

mtk
fonte
@ l0b0 Você editado fora observação do OP que os números de linha são "lá apenas para explicação" ...
jasonwryan
@jasonwryan Desculpe, eu pensei que as linhas estavam lá para explicação. Erro de análise na linha 0.
l0b0
stackoverflow.com/questions/9605232/merge-two-lines-into-one
Ciro Santilli新疆改造中心法轮功六四事件

Respostas:

39

Basta usar cat(se você gosta de gatos ;-)) e paste:

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

Explicação: pastelê de vários arquivos e cola as linhas correspondentes (linha 1 do primeiro arquivo com linha 1 do segundo arquivo etc):

paste file1 file2 ...

Em vez de um nome de arquivo, podemos usar -(traço). pastepega a primeira linha do arquivo1 (que é stdin). Então, ele deseja ler a primeira linha do arquivo2 (que também é stdin). No entanto, uma vez que a primeira linha do stdin já foi lida e processada, o que agora espera no fluxo de entrada é a segunda linha do stdin, que pastecola felizmente na primeira. A -dopção define o delimitador para ser uma vírgula e não uma guia.

Como alternativa, faça

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

PS Sim, pode-se simplificar o acima para

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

ou

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

qual tem a vantagem de não usar cat.

No entanto, eu não usei esse idioma de propósito , por razões de clareza - é menos detalhado e eu gosto cat(GATOS SÃO AGRADÁVEIS). Então, por favor, não edite.

Como alternativa, se você preferir colar a gatos (colar é o comando para concatenar arquivos horizontalmente, enquanto gato os concatena verticalmente), você pode usar:

paste file.in | paste -d, - -
janeiro
fonte
Só para mencionar novamente. Os números de linha não são uma parte do arquivo :)
mtk
O paste comando funciona perfeitamente, você pode dar um pouco mais de explicação sobre isso? Os hífens ???
mtk
2
Os hífens significam "ler de stdin". Se a mesma fonte de entrada for repetida, a pasta sabe ler várias vezes por linha de saída.
dubiousjim
@sch: edição legal, não vou tocá-lo :-)
janeiro
11
Com relação ao seu catargumento. Não sed "N;s/\n/,/" file.in > file.outfunciona?
Bernhard
8

Caso alguém que esteja aterrissando aqui esteja procurando combinar todas as linhas em um forro CSV único, tente

cat file | tr '\n' ','
Darren Weber
fonte
3
sed 'N;s/\n/,/' file

Usando sed, junte-se a (N) a cada 2 linhas e substitua a nova linha (\ n) por ",".

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

Observe também que, como estamos apenas substituindo um caractere por outro (todas as outras novas linhas por vírgula), podemos trabalhar no arquivo de entrada em vigor:

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

(mas cuidado, pode não funcionar em sistemas não Unix que possuem terminadores CRLF (como os da Microsoft) que alguns POSIX emulados pastepodem tratar de maneira não-Unix)

Stéphane Chazelas
fonte
O que isso 1está fazendo aqui 1<>? isso é um erro de digitação?
α 19sнιη
@ αғsнιη, veja isso
iruvar
@iruvar thank you
αғsнιη
2

Aqui está uma linha (embora potencialmente milhões de comandos executados) usando o Bash puro:

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

Eu uso um subshell (a parêntese) para não precisar armazenar e restaurar IFS. Qual deles deve ser feito para não atrapalhar o ambiente dos usuários, caso a fonte seja originada. A alternativa seria passar esse novo IFS apenas para readcomo IFS= read -r name,IFS= read -r code .

O fato de todos os comandos do loop serem construídos no shell torna seu desempenho aceitável e é ainda mais rápido que as outras soluções para arquivos pequenos. Mas muitas pessoas consideram isso uma prática ruim e é preciso ter cuidado ao generalizá-la para qualquer outra coisa.

Excluído
fonte
em geral yay por usar subshells para localizar mudanças no ambiente. Mas, neste caso, não é necessário: você pode fazê-lo while IFS='\n' read -r name; do IFS='\n' read -r code ... done < file.in, que é um idioma que frequentemente vejo nos scripts de shell. A -rsinalização para readsignifica "interpretar o caractere '\' seguido pelo caractere 'n' no fluxo stdin como dois caracteres, e não como uma nova linha". Indiscutivelmente, pode ser mais estético criar o subshell do que repetir IFS='\n'.
dubiousjim
@dubiousjim: -rMelhorou a solução tecnicamente. Ótimo! Não sou fã da ideia de passar IFSduas vezes alterado . Se eu tivesse usado uma leitura, super legal, mas não duas vezes. Claro que isso é uma questão de opinião . Usar um subshell é um pouco acima do conhecimento geral do Bash, eu diria, então muitas pessoas terão problemas para entender seu objetivo. Isso é uma coisa ruim.
Excluído
2

Para o conjunto completo de respostas, uma awksolução possível pode ser:

awk 'NR%2==1 {printf $0","} NR%2==0 { print $0}' *file*
Bernhard
fonte
@ downvoter: O que há de errado com a minha resposta para merecer um voto negativo? Como pode ser melhorado?
Bernhard
Talvez porque o preguiçoso printf? Raramente falhará quando um nome de estação contiver um especificador de formato. (Veja pastebin.com/wgxFttrJ para um exemplo.) Mas isso é apenas um palpite, o voto negativo não é meu.
manatwork
1

Hoary castanha velha de um awkidioma

awk '{ORS=NR%2?",":"\n";print}' file
Station Name,Station Code
A N DEV NAGAR,ACND
ABHAIPUR,AHA
ABOHAR,ABS
ABU ROAD,ABR
iruvar
fonte
awk '{ORS=NR%2?",":"\n"};1'é mais curto e mais idioma
cuonglm
@cuonglm, eu duvido. Nesse caso, ainda é uma linha única, apesar printda intenção e é clara. 1é tão claro para velhas awkmãos, como eu, mas eu prefiroprint
Iruvar
Esta foi a primeira solução simples que achei fácil de configurar em mais de 2 linhas. Eu lutei sedpor um tempo antes de pesquisar, mas awkfacilitei a combinação a cada 4 linhas. Me salvou uma viagem para o $EDITOR!
opello
0

Possível com perl também,

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

margarida
fonte
0

Por exemplo:

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

Saída: (nota: xargs -L number_of_columnsfunciona bem com quase qualquer número de colunas, não apenas a cada duas linhas)

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
jmunsch
fonte