Como mesclar linhas com intervalo fixo em um arquivo?

4

Eu tenho um arquivo e seu conteúdo é assim:

a1
b1
c1
aa
bb
cc
aaa
bbb
ccc
d1
e1
f1
dd
ee
ff
ddd
eee
fff
g1
h1
i1
gg
hh
ii
ggg
hhh
iii

Qual é a melhor maneira de mesclar as linhas com intervalo fixo (3 neste caso) e obter algo como:

a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

O algoritmo para obter a saída da entrada é:

  • Primeiro, obtemos a linha 1, que é a1.
  • Sabemos que o intervalo é de 3
  • Portanto, linha 1, linha (1 + 3), linha (1 + 3 + 3) deve estar na mesma linha
  • Da mesma forma, as linhas 2, 5 e 8 devem estar na mesma linha, etc.

Aqueles a1 , aa e aaa etc. são apenas texto fictício aleatório e podem ser qualquer string aleatória. O ponto é que existe um intervalo fixo entre a1 , aa e aaa .

Atualmente, uso a macro de teclado do emacs para executar essa tarefa. No entanto, quero saber se existem maneiras melhores de resolver esse problema. Desde já, obrigado.

    
por Ogrish Man 26.09.2016 / 19:59

4 respostas

11

Se você estiver em gnu / anything e o número de linhas for múltiplo de 9, você poderá executar

split -l9 --filter='pr -3 -s" " -t' infile

Isso divide a entrada em pedaços de nove linhas e cada peça é canalizada para pr -3 -s" " -t' , o que a coloca em colunas ... Dependendo do não. de linhas e seu comprimento, você pode precisar jogar com pr options -w e -l . Veja a página man para mais detalhes.

    
por 26.09.2016 / 20:39
6

Aqui está uma solução simplista em awk, hard-coded para inserir três conjuntos em intervalos de três linhas:

{
  if (NR > 1 && (NR % 9) == 0) {
    print a "\n" b "\n" c " " $0
    a=""
    b=""
    c=""
  } else if (NR % 3 == 1) {
    if (NR % 9 > 1) {
      a=a" "$0
    } else {
      a=$0
    }
  } else if (NR % 3 == 2) {
    if (NR % 9 > 2) {
      b=b" "$0
    } else {
      b=$0
    }
  } else {
    if (NR % 9 > 3) {
      c=c" "$0
    } else {
      c=$0
    }
  }
}

Salve isso em um arquivo e execute awk -f thatfile < input . Tenho certeza de que existem maneiras mais inteligentes de fazê-lo, mas não trabalho no awk todos os dias.

    
por 26.09.2016 / 20:35
4

É um pouco complicado. Eu não sei de um único utilitário que possa fazer isso:

Esse canal (essencialmente) lê 9 linhas por vez e usa pr para formatar em 3 colunas:

# there are 9 single hyphens below
paste -d: -- - - - - - - - - - < file | while read line; do
    tr : '\n' <<<"$line" | pr -s" " -T -3
done
a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

Isso pressupõe que você não tenha dois pontos em seu texto real.

    
por 26.09.2016 / 20:42
1

Uma maneira muito simples e clara em TXR :

@(repeat)
@x0
@x1
@x2
@y0
@y1
@y2
@z0
@z1
@z2
@  (output)
@x0 @y0 @z0
@x1 @y1 @z1
@x2 @y2 @z2
@  (end)
@(end)

Executar:

$ txr reshape.txr data
a1 aa aaa
b1 bb bbb
c1 cc ccc
d1 dd ddd
e1 ee eee
f1 ff fff
g1 gg ggg
h1 hh hhh
i1 ii iii

Existem maneiras de condensar isso, mas você precisa trabalhar um pouco mais para compreendê-las, como:

@(repeat)
@  (collect :times 9)
@line
@  (end)
@  (bind (x y z) @(tuples 3 line))
@  (output)
@    (repeat)
@x @y @z
@    (end)
@  (end)
@(end)

Além disso, como alguém que sabe o que está fazendo no Awk pode implementar:

        { a[(NR-1)%9] = $0 }
!(NR%9) { print a[0], a[3], a[6]
          print a[1], a[4], a[7]
          print a[2], a[5], a[8] }

Saída:

$ awk -f reshape.awk data
a1 aa aaa
[ ... ]
i1 ii iii

E se esse codificador encontrar repetidos padrões print repugnant:

        { a[(NR-1)%9] = $0 }
!(NR%9) { for (i = 0; i < 3; i++)
            print a[i], a[i+3], a[i+6] }

Solução TXR Lisp:

[(opip (tuples 3) (tuples 3) (mappend transpose)
       (mapcar (aret '@1 @2 @3')) tprint)
 (get-lines)]

Executar:

$ txr reshape.tl < data

Na linha de comando: use -t , elimine o tprint :

$ txr -t '[(opip (tuples 3) (tuples 3) (mappend transpose)
                 (mapcar (aret '@1 @2 @3')))
           (get-lines)]' < data

Isso funciona colocando a entrada em um pipeline que a transporta para trigêmeos, em seguida, trigêmeos desses trios (basicamente matrizes 3x3 feitas de listas aninhadas). Essas matrizes são transpostas individualmente e suas linhas são então anexadas para formar uma lista gigante de trigêmeos. Esses trios são transformados em strings com o operador de aplicativo aret partial como uma interpolação de strings e a saída com tprint , que trata listas de strings como linhas para saída. A sintaxe

(aret '@1 @2 @3')

expande-se para algo parecido com

(lambda (. args)
  (apply (lambda (arg1 arg2 arg3)
           '@arg1 @arg2 @arg3')
         args))

Basicamente, ele cria implicitamente uma função anônima de um argumento que trata seu argumento como uma lista de argumentos para aplicar a uma função anônima de 3 argumentos em que @1 , @2 e @3 denotam os argumentos. O corpo da função é derivado da expressão quasi-string original, substituindo esses parâmetros numéricos especiais por nomes de argumentos gerados por máquina.

    
por 28.09.2016 / 04:33