dividir um arquivo por um caractere e adicioná-lo aos dados

2

Eu tenho uma pergunta que gostaria de tornar mais simples, como faço para dividir o segundo campo de um arquivo (delimitado por espaço) por um sublinhado e adicionar a segunda parte como um novo campo para os dados (saída como guia)?

por exemplo. file.txt

2 1_123
2 2_345

out.tab :

2 1_123 123
2 2_345 345

Eu tenho que trabalhar com isso, mas existe uma maneira mais simples? Isso parece muito confuso ...

paste -d' ' file.txt <(cat file.txt | cut -d'_' -f 2- ) | tr [:blank:] \t > out.temp && mv out.temp out.tab
    
por jamespower 04.12.2015 / 00:54

3 respostas

2

Todas as suas transformações estão misturando dados dentro da mesma linha do arquivo, então seria muito mais simples usar uma ferramenta que manipula dados linha por linha, como sed ou awk.

sed -e 's/^[^ \t]*[ \t][^ \t]*_\([^_ \t]*\)/&\t/' -e 'y/ /\t/' <<<'2 1_123'

Explicação: use uma expressão regular para corresponder ao primeiro campo (até o primeiro espaço ou tabulação), o segundo campo até seu último sublinhado e o segundo campo após o último sublinhado (que é colocado em um \(…\) grupo para que possa ser usado no texto de substituição). Mantenha o mesmo texto ( & na substituição) seguido pelo conteúdo do grupo correspondente ( ). Qualquer campo após o segundo é deixado inalterado. Por fim, substitua todos os espaços por tabulações.

Se o seu sed não suportar \t para significar uma guia, use um caractere de tabulação literal.

    
por 04.12.2015 / 01:03
2

Para o exemplo simples que você mostra onde há apenas um sublinhado e que está no segundo e no último campo, você pode fazer:

$ sed 's/_\(.*\)/& /' file | tr ' ' '\t'
2   1_123   123
2   2_345   345

Ou, se a sua implementação sed suportar expressões regulares estendidas:

$ sed -E 's/_(.*)/& /' file | tr ' ' '\t'
2   1_123   123
2   2_345   345

Isso corresponderá ao primeiro _ e tudo depois disso. Os parênteses capturam a string combinada e podemos nos referir a ela na mão direita da substituição como . & é tudo o que foi correspondido, então o _ seguido pelo restante do segundo campo. A substituição, portanto, será impressa, um espaço e os caracteres após o _ . O tr substitui todos os espaços por tabulações.

Para casos mais complexos, em que você pode ter um número arbitrário de campos e qualquer um deles pode conter _ , você pode usar perl :

$ perl -lane 's/ +/\t/g; $F[1]=~/_(\S+)/; print "$_\t$1"' file 
2   1_123   123
2   2_345   345

O -a faz com que perl divida sua entrada no espaço em branco na matriz @F . O segundo campo é $F[1] porque as matrizes começam a contar em 0 . O -n significa "leia o arquivo de entrada linha a linha e aplique o script fornecido por -e ". O -l remove as novas linhas à direita da linha de entrada e adiciona uma nova linha a cada chamada print .

s/ +/\t/g; substitui todas as ocorrências de um ou mais espaços por tabulações, $F[1]=~/_(\S+)/; corresponde aos caracteres após o _ no segundo campo e os salva como $1 e o print "$_\t$1" imprimirá a linha atual ( $_ ) seguido pelo que quer que tenha sido correspondido.

Outra ferramenta útil para dados baseados em campo é awk :

$ awk '{gsub(/ /,"\t");l=$2; sub(/.*_/,"",l); print $0"\t"l}' file 
2   1_123   123
2   2_345   345

Em awk , as linhas de entrada são divididas automaticamente no espaço em branco e se tornam $1 m $2 ... $N . O gsub(/ */,"\t"); substitui todos os espaços por tabulações; l=$2 salva o segundo campo como l ; sub(/.*_/,"",l); remove tudo até e incluindo o _ de l ; e print $0"\t"l imprime a linha ( $0 ) seguida por uma tabulação e o segundo campo modificado.

    
por 04.12.2015 / 01:30
2
t=$(printf \t)
sed "s/[^ _]*\(_\([^ _]*\)\)\{0,1\}[^ ]*/& /2;s/  */$t/g" <in >out

... deve funcionar para qualquer número de _ no segundo campo ou em qualquer número de campos. Ele converte qualquer sequência de espaços em um único caractere de tabulação. se dois espaços consecutivos contarem como dois delimitadores de campo, use:

y/ /$t/

... no final, em vez de ...

s/  */$t/

... substituição.

    
por 04.12.2015 / 03:22