Manipulação de texto com sed

12

Atualmente, tenho vários arquivos de texto com conteúdo parecido com este (com muitas linhas):

565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

Desejo alterar cada linha para ter o seguinte formato:

0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Existe alguma maneira de fazer isso usando o sed? Ou preciso recorrer ao Python?

Zanna
fonte

Respostas:

22

Você poderia fazer isso com o sed, sim, mas outras ferramentas são mais simples. Por exemplo:

$ awk '{
        printf "%s ", $2; 
        for(i=3;i<=NF;i++){
            printf "%s:%s:1 ",$1,$(i) 
        }
        print ""
       }' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Explicação

awk irá dividir cada linha de entrada no espaço em branco (por padrão), economizando cada campos como $1, $2, $N. Então:

  • printf "%s ", $2; imprimirá o segundo campo e um espaço à direita.
  • for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }: iterará nos campos 3 até o último campo ( NFé o número de campos) e, para cada um deles, imprimirá o 1º campo, a :, o campo atual e a :1.
  • print "" : isso apenas imprime uma nova linha final.

Ou Perl:

$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file 
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1 

Explicação

As -amarcas perlse comportam como awke dividem sua entrada no espaço em branco. Aqui, os campos são armazenados na matriz @F, o que significa que o 1º campo será $F[0], o 2º $F[1]etc. Então:

  • print "$F[1] " : imprime o segundo campo.
  • print "$F[0]:$_:1 " for @F[2..$#F];: repita os campos 3 até o último campo ( $#Fé o número de elementos na matriz @F, portanto, @F[2..$#F]pega uma fatia da matriz que começa no terceiro elemento até o final da matriz) e imprime o 1º campo, a :, depois o campo atual e um :1.
  • print "\n" : isso apenas imprime uma nova linha final.
Terdon
fonte
12

Aqui está um horrível sed caminho!

$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Mais facilmente:

sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a 
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'

Notas

  • -r use ERE
  • s/old/new/substitua oldpornew
  • ^([0-9]+) salve alguns números no início da linha
  • \1 referência anterior ao primeiro padrão salvo
  • :a rotule esta seção do script a
  • ( |$) um espaço ou o fim da linha
  • t teste se a última substituição foi bem-sucedida - se foi, execute o próximo comando
  • aencontre o rótulo :ae faça-o novamente
  • s/ $// remova o espaço à direita

Então, depois de adicionar a estrutura à primeira parte, encontramos repetidamente a última instância da estrutura e a aplicamos ao próximo número ...

Mas concordo que outras ferramentas facilitam ...

Zanna
fonte
Eu estava esperando sua solução sed: D
Ravexina 5/17/17
: D ele me levou um tempo @Ravexina - Acho Muru pode fazer um aspirador
Zanna
5

Com awk:

awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file

ou com bash:

while read -r -a a; do                  # read line to array a
  printf "%s " ${a[1]}                  # print column #1
  for ((i=2;i<${#a[@]};i++)); do        # loop from column #2 to number of columns
    printf "%s " "${a[0]}:${a[$i]}:1"   # print content/values
  done
  echo                                  # print line break
done < file                             # read file from stdin

Resultado:

0 565: 10: 1 565: 12: 1 565: 23: 1 565: 18: 1 565: 17: 1 565: 25: 1 
1 564: 7: 1 564: 12: 1 564: 13: 1 564: 16: 1 564: 18: 1 564: 40: 1 564: 29: 1 564: 15: 1 
Cyrus
fonte
5

Bem, você pode fazê-lo no sed, mas o python também funciona.

$ ./reformatfile.py  input.txt                                                                        
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1

Os conteúdos do reformatfile.pysão os seguintes:

#!/usr/bin/env python3
import sys

with open(sys.argv[1]) as fd:
    for line in fd:
        words = line.strip().split()
        pref = words[0]
        print(words[1],end=" ")
        new_words = [ ":".join([pref,i,"1"]) for i in words[2:] ]
        print(" ".join(new_words))

Como é que isso funciona? Não há realmente nada de especial acontecendo. Abrimos o primeiro argumento da linha de comando como arquivo para leitura e prosseguimos com a divisão de cada linha em "palavras" ou itens individuais. As primeiras palavras se tornam prefvariáveis ​​e imprimimos no segundo item stdout (palavras [1]) que termina com espaço. A seguir, construímos um novo conjunto de "palavras" por meio da compreensão da lista e .join()funcionamos em uma lista temporária de pref, cada palavra e string "1". O passo final é imprimi-los

Sergiy Kolodyazhnyy
fonte
4

Com awk:

awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i);\
          printf("%s:%s:1\n", $1, $NF)}' file.txt

É tudo sobre formatação de campos separados por espaço no formato desejado:

  • printf("%s ", $2) imprime o segundo campo com um espaço à direita

  • for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i) itera sobre o terceiro ao segundo último campo e imprime os campos no formato desejado (primeiro campo, depois dois pontos, depois o campo atual, depois dois pontos, finalmente 1) com um espaço à direita

  • printf("%s:%s:1\n", $1, $NF) imprime o último campo com nova linha

Exemplo:

% cat file.txt
565 0 10 12 23 18 17 25
564 1 7 12 13 16 18 40 29 15

% awk '{printf("%s ", $2); for(i=3; i<NF; i++) printf("%s:%s:1 ", $1, $i); printf("%s:%s:1\n", $1, $NF)}' file.txt
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
heemail
fonte