Mesclar dois arquivos linha a linha com o símbolo de tubo triplo delimitador “|||”

13

Eu tenho dois arquivos paralelos com o mesmo número de linhas em dois idiomas e planejo mesclar esses dois arquivos linha por linha com o delimitador ||| . Por exemplo, os dois arquivos são os seguintes:

Arquivo A:

1Mo 1,1 I love you.
1Mo 1,2 I like you.
Hi 1,3 I am hungry.
Hi 1,4 I am foolish.

Arquivo B:

1Mo 1,1 Ich liebe dich.
1Mo 1,2 Ich mag dich.
Hi 1,3 Ich habe Durst.
Hi 1,4 Ich bin neu.

A saída esperada é assim:

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.

Eu tentei o comando paste , como:

paste -d "|||" fileA fileB

Mas a saída retornada contém apenas um canal, como:

1Mo 1,1 I love you. |1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. |1Mo 1,2 Ich mag dich.

Existe alguma maneira de separar cada par de linhas pelo tubo de tripas ||| ?

    
por hui 23.11.2015 / 11:22

5 respostas

18

Com a pasta POSIX :

:|paste -d ' ||| ' fileA - - - - fileB

paste concatenará as linhas correspondentes de todos os arquivos de entrada. Aqui temos seis arquivos, fileA , quatro arquivos fictícios do padrão em - e fileB .

A lista de delimitadores inclui um espaço, três tubos e um espaço nessa ordem será usado por paste circularmente.

Para a primeira linha de seis arquivos, fileA será concatenado com o primeiro arquivo fictício (o que não é nada, graças ao no-op: operator), produza line1-fileA<space> .

O primeiro arquivo dummy será concatenado com o segundo por um pipe, produza line1-fileA | , então o segundo arquivo fictício com o terceiro arquivo fictício, produza line1-fileA || , o terceiro arquivo fictício com o arquivo fictício, produza line1-fileA ||| .

E o quarto arquivo fictício com fileB produz line1-fileA ||| line1-fileB .

Esse passo será repetido para todas as linhas, dando-lhe o resultado esperado.

O uso de :| é para menos digitação e principalmente para uso no shell interativo. Em um script, você deve usar:

</dev/null paste -d ' ||| ' fileA - - - - fileB

para evitar que uma subshell seja gerada.

    
por 23.11.2015 / 11:32
7

Bem, isso não usa sed, awk ou grep, mas você pode fazê-lo facilmente no bash. O comando é:

(while IFS= read -r a <&3 && IFS= read -r b <&4; do echo "$a ||| $b"; done) 3<fileA 4<fileB

O problema com o colar é que o delimitador é um único caractere. Você também pode inserir um único caractere e usar sed para transformá-lo, mas isso seria propenso a erros se o caractere já aparecesse no arquivo de entrada.

    
por 23.11.2015 / 11:29
5

Uma versão do awk (GNU)

awk '{printf ("%s ||| ", $0); getline < "fileB"; print $0 }' fileA

Com o comando getline em awk , você pode definir $0 (todas as variáveis para colunas) do próximo registro de entrada, se getline < "filename" definir o próximo $0 do arquivo especificado.

getline < "file" Set $0 from next record of file; set NF.

Por que sua tentativa não funcionou como você espera? De man paste , podemos ler

-d, --delimiters=LIST
     reuse characters from LIST instead of TABs

mas usa os delimitadores um para cada coluna .

Então o comando paste -d '|*|*' fileA fileB fileA fileB me dá linhas como

Hi 1,3 I am hungry.|Hi 1,3 Ich habe Durst.*Hi 1,3 I am hungry.|Hi 1,3 Ich...
Hi 1,4 I am foolish.|Hi 1,4 Ich bin neu.*Hi 1,4 I am foolish.|Hi 1,4 Ich...

Uma solução sed que sugiro evitar, mesmo que próxima da sua tentativa original, porque corrige o comportamento obtido para o seu propósito original:

 paste -d '|' fileA fileB | sed 's/|/|||/g'

Para evitar, você substitui cada padrão | pelo novo ||| , , mas é preciso presumir que o símbolo do canal ( | ) não está presente em seus dados . você tem que lidar com casos especiais e tornar mais complexo o código para evitar efeitos colaterais.

Uma variante com a construção aqui String [ 1 ] <<<

 paste -d ' ||| ' fileA - - - - fileB  <<< ''

Você define 5 delimitadores com -d ' ||| ' (espaço, |, |, espaço) e 4 arquivos fictícios ( - - - - ) que coletarão dados da string vazia '' .

Testado no GNU Awk 4.0.1, cole (GNU coreutils) 8.21 e sed (GNU sed) 4.2.2

    
por 23.11.2015 / 11:55
4

Se você quiser evitar a magia e o drama de delimitadores circulares e arquivos fictícios, você poderia apenas acrescentar seu delimitador a um arquivo antes colando-os:

paste <(sed 's/$/ |||/' filea) fileb

1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. |||    Hi 1,4 Ich bin neu.
    
por 27.11.2015 / 09:33
0

você também pode fazer isso em Python dessa maneira.

lines1 = [ line.rstrip() for line in open("file1") ]
lines2 = [ line.rstrip() for line in open("file2") ]
for i in xrange((len(lines1))): print lines1[i] + " ||| " + lines2[i]
... 
1Mo 1,1 I love you. ||| 1Mo 1,1 Ich liebe dich.
1Mo 1,2 I like you. ||| 1Mo 1,2 Ich mag dich.
Hi 1,3 I am hungry. ||| Hi 1,3 Ich habe Durst.
Hi 1,4 I am foolish. ||| Hi 1,4 Ich bin neu.
    
por 26.11.2015 / 20:01