Altera a ordem das linhas em um arquivo

11

Estou tentando alterar a ordem das linhas em um padrão específico. Trabalhando com um arquivo com muitas linhas (ex. 99 linhas). Para cada três linhas, gostaria que a segunda linha fosse a terceira e a terceira a segunda.

EXEMPLO.

1- Entrada:

gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.
...

2- Saída:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
...
    
por Annick Raymond 31.05.2017 / 23:45

9 respostas

12

Usando awk e matemática inteira:

awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay; delay=""} END { if(length(delay) != 0 ) { print delay } }' /path/to/input

O operador de módulo executa a divisão inteira e retorna o restante, então, para cada linha, ele retornará a sequência 1, 2, 0, 1, 2, 0 [...]. Sabendo disso, apenas salvamos a entrada nas linhas onde o módulo é 2 para mais tarde - a saber, logo após imprimir a entrada quando ela é zero.

    
por 31.05.2017 / 23:50
23
$ seq 9 | sed -n 'p;n;h;n;G;p'
1
3
2
4
6
5
7
9
8

Ou seja, p rint a linha atual, obtenha o n ext one, h old, obtenha o n ext one, G et a linha mantida (anexe-o ao espaço de padrão ) e p rint que espaço padrão de 2 linhas com a terceira e segunda linhas trocadas.

    
por 01.06.2017 / 00:21
3

Outra abordagem do awk :

awk '{print $0; if ((getline L2)>0 && (getline L3)>0){ print L3 ORS L2 }}' file

A saída:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
  • (getline L2)>0 && (getline L3)>0 - extrai os próximos 2 registros, se existirem

  • cada segundo e terceiro registros são atribuídos às variáveis L2 e L3 , respectivamente

por 01.06.2017 / 00:08
1

Usando perl e um script curto:

user@pc:~$ cat input.txt 
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

user@pc:~$ perl -ne '$l2=<>; $l3=<>; print $_,$l3,$l2;' input.txt 
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

O script processa o arquivo inteiro, para cada linha (armazenada em $_ ) ele obterá as próximas duas linhas ( $l2 e $l3 ) e as imprimirá na ordem solicitada: line1, line3, line2.

    
por 01.06.2017 / 00:12
1

Um caminho pode ser o seguinte:

sed -e '
   /\n/s/\(.*\)\(\n\)\(.*\)//;//b
   $!N;$q;N;                            # load up the pattern space with 3 lines provided eof not reached
   P;D;                                 # first just print the first line then interchange the two and print them
' yourfile

Alternativamente,

perl -ne 'print $_, reverse scalar <>, scalar <>' yourfile

Resultados

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
    
por 31.05.2017 / 23:55
1

Por que não criar um loop while? Na forma expandida:

( while read a
  do
    read b
    read c
    echo "$a"
    echo "$c"
    echo "$b"
  done
) < input.txt

Em "formato de linha única":

( while read a ; do read b ; read c ; echo "$a" ; echo "$c" ; echo "$b" ; done) < input.txt

Saídas:

gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.
    
por 01.06.2017 / 14:02
1

Perl

perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt

A idéia aqui é que usamos o operador de módulo % com o número de linha $. variable, para descobrir qual é a primeira, qual é a cada segundo e qual é a cada terceira linha. Para cada 3ª linha, o resto é 0, enquanto para cada 1ª e 2ª linha, terá os números correspondentes.

Teste:

$ cat input.txt                                                                                                          
gi_1234
My cat is blue.
I have a cat.
gi_5678
My dog is orange.
I also have a dog.

$ perl -ne 'print if $.%3==1;$var=$_ if $.%3==2;print $_ . $var if $.%3==0' input.txt                                    
gi_1234
I have a cat.
My cat is blue.
gi_5678
I also have a dog.
My dog is orange.

Melhoria menor

A abordagem com o armazenamento da segunda linha em uma variável tem uma falha. E se a última linha for a "segunda", ou seja, para esse número de linha o restante é 2? O código original na resposta do meu e do DopeGhoti não imprimirá My dog is orange se deixarmos de fora a última linha. A correção para isso nos dois casos é usar o bloco de código END{} , com a desativação da variável temporária após a impressão. Em outras palavras:

$ awk 'NR%3 == 1 { print } NR%3 == 2 { delay=$0 } NR%3 == 0 { print; print delay;delay=""}END{print delay}' input.txt

e

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s}' input.txt 

Dessa forma, o código funcionará para um número arbitrário de linhas em um arquivo, não apenas aquelas divisíveis por 3.

Correção adicional para problemas mencionados nos comentários

No caso do awk, se a última linha do arquivo produzir uma saída de 1 para $. % 3, o código anterior tem problema de gerar uma nova linha em branco devido à impressão incondicional de END{print delay} , pois a função print mencionada nos comentários sempre anexa nova linha a qualquer variável em que esteja operando. No caso de perl versão, esse problema não ocorre, pois com -ne flags print function não acrescenta a nova linha.

No entanto, a correção no caso do awk é tornar condicional, como mencionado por Dope Ghoti nos comentários é para verificar o tamanho da variável temporária. A versão perl da mesma correção seria:

$ perl -ne '$s=$_ if $.%3==2;print $_ . $s and $s="" if $.%3==0 or $.%3==1;END{print $s if length $s}' input.txt 
    
por 01.06.2017 / 04:10
1

Vim

Não é adequado para arquivos longos, mas ainda útil se você estivesse editando um arquivo e desejasse, por exemplo, reordenar algumas estrofes yaml.

Primeiro, grave uma macro:

gg qq j ddp j q

Em seguida, repita o número de vezes desejado:

@q @q @q ...

Ou apenas, por exemplo,

3@q

Explicação:

  • gg - vai para a primeira linha
  • qq - começa a gravar uma macro
  • j - vai para a segunda linha
  • ddp - troca a segunda e a terceira linha
  • j - vai para a quarta linha, ou seja, para a primeira das próximas três linhas
  • q - para a gravação
  • @q - reproduz a macro uma vez
  • 3 @ q - reproduz a macro três vezes
por 01.06.2017 / 15:40
0

Uso: ./shuffle_lines.awk input.txt

Verifique shebang #!/usr/bin/awk -f , porque a localização awk pode ser diferente no seu sistema.

#!/usr/bin/awk -f

{
    if ((NR + 1) % 3 == 0) {
        buffer = $0;
    } else if (NR % 3 == 0) {
        print $0 ORS buffer;
        buffer = "";
    } else {
        print;
    }
}
    
por 01.06.2017 / 20:04