Extrai linhas usando um delimitador e acrescenta como coluna em um arquivo

2

Eu tenho um arquivo que contém dados como:

a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 

... e eu preciso da saída como:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

Eu posso fazer isso usando python, mas eu quero tentar isso usando scripts de shell. Eu estava pensando em primeiro isolar as linhas que contêm o delimitador ',' e, em seguida, trabalhe adiante. Eu usei isso até agora para isolar as linhas:

perl -F, -ane 'print if $#F >=1' filename

... mas estou preso no próximo passo.

    
por Anurag 12.06.2018 / 08:51

8 respostas

3

com perl

$ perl -lane '@v=split/,/,$F[1]; $i=0;
              print "$_ $v[$i++]" for split/,/,$F[0]' ip.txt
a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8

Divida a primeira / segunda coluna, inicialize o contador de índice e, em seguida, imprima os pares iterando a divisão da outra coluna

A opção

-a dividirá automaticamente a linha de entrada nos espaços em branco e terá os resultados em @F array

    
por 12.06.2018 / 09:30
2
Solução de

Awk (assumindo que o número de "chaves" (contido no primeiro campo $1 ) corresponderia sempre ao número de "valores" (contido no 2º campo $2 )):

awk '$1 ~ /,/{
         len = split($1, keys, ",");
         split($2, vals, ",");
         for (i = 1; i <= len; i++) print keys[i], vals[i];
         next
    }1' file

A saída:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
    
por 12.06.2018 / 09:07
2

Uma maneira de fazer isso usando o editor sed é:

sed -e '
   s/,/\n/
   s/\(\n.*[[:blank:]]\)\([^,]*\),/ /
   P;D
' input.file

Trabalhando:

  • Recorte o elemento principal separado por vírgulas do segundo campo.
  • Em seguida, anexe esse elemento ao elemento separado por vírgula líder do primeiro campo.
  • Imprima o elemento principal do primeiro campo e exclua-o depois disso.
  • Repita este procedimento com o que resta no espaço do padrão até que esteja vazio.

Outro método usando o Perl é:

perl -lane '
   my($kref, $vref, %h) = map { [split /,/] } @F[0,1];
   @h{@$kref} = @$vref;
   print "$_  $h{$_}" for @$kref;
' input.file

Outra maneira é mostrada aqui:

perl -lpe 'print "$1 $3" while s/^([^,]*),(.*\h)([^,]*),/$2/' input.file

Trabalhando:

  • Olhe para o regex desta forma: (Perl lê em uma linha de cada vez do arquivo) então:
    • ^ ([^,] *) escolherá o elemento principal separado por vírgulas da linha atual. Isso é armazenado na variável $ 1.
    • (. * \ h) preservará, para a próxima iteração do loop while, o conteúdo intermediário que começa no segundo elemento separado por vírgulas do primeiro campo até o início do segundo elemento separado por vírgulas. do segundo campo. Isso é armazenado na variável $ 2.
    • ([^,] *) deve selecionar o elemento principal separado por vírgula do segundo campo da linha atual. Isso é armazenado na variável $ 3.
    • Agora, "$ 1 $ 3" é impresso no STDOUT e a linha é reduzida para $ 2. O laço while agora executa a operação novamente nesta linha editada, que é $ 2 da linha anterior, ..... isto se repete até que o s /// seja bem sucedido. O fracasso vem quando ficamos sem vírgulas. Nesse ponto, o que permanece na linha, "c 5" é impresso em STDOUT pelo comportamento padrão de perl no modo -p.
  • Arranque os elementos principais separados por vírgulas dos primeiro e segundo campos.
  • Imprima esses elementos e também reduza o registro atual removendo.
  • Faz um loop sobre o registro atual enquanto está tendo 2 vírgulas.
  • O último par é impresso automaticamente devido à opção -p do Perl.
perl -lane '
   my($kref, $vref) = map { [split /,/] } @F;
   print shift @$kref, " ", shift @$vref while @$kref && @$vref;
' input.file

Trabalhando:

    As chaves
  • são armazenadas em uma matriz @ $ kref, valores correspondentes em @ $ vref. Não há hashes envolvidos aqui.
  • imprima a parte superior das matrizes simultaneamente e, em seguida, remova a parte superior ... enxágue, repita enquanto as duas matrizes não estão vazias.

Saída:

a 1
b 2
c 3
d 4
e 5
f 6
g 7
h 8
    
por 12.06.2018 / 12:09
1

Usando o awk:

awk '{gsub(","," "); for(i=0;i<NF/2;i++) print $(i+1),$(i+1+NF/2)}' file     

Após remover a vírgula de cada linha, o script percorre metade dos parâmetros de cada linha para imprimir os primeiros campos junto com o campo da segunda metade da linha.

    
por 12.06.2018 / 09:27
1

"usando shell script" - isso é bash:

while read -r key value; do
    IFS=, read -ra keys <<<"$key"
    IFS=, read -ra vals <<<"$value"
    for ((i=0; i < ${#keys[@]}; i++)); do
        echo "${keys[i]} ${vals[i]}"
    done
done <<END
a 1
b 2
c,d,e 3,4,5
f 6
g,h 7,8 
END
    
por 12.06.2018 / 16:04
0

Com o gnu sed

sed -E ':A;s/([^,]*),([^ ]*) ([^,]*),(.*)/ \n /;tA' infile
    
por 12.06.2018 / 10:18
0

Desculpe pela ofuscação ...

perl -pe '1 while s/(.*),(.*\h)(.*),/$1 $3\n$2/' infile

Cada substituição extrai o último par:

a,b,c 1,2,3  →  a,b 1,2  →  a 1
                c 3         b 2
                            c 3
    
por 12.06.2018 / 13:48
0

Existem muitas soluções aqui programadas em perl, awk, sed, bash, ... mas nenhuma solução de script.

# /bin/bash
f="yourFile"
paste -d' ' <(cut -d' ' -f1 "$f" | tr , '\n') <(cut -d' ' -f2 "$f" | tr , '\n')

ou escrito de forma diferente

# /bin/bash
f() { cut -d' ' -f"$1" yourFile | tr , '\n'; }
paste -d' ' <(f 1) <(f 2)
    
por 13.06.2018 / 11:06