Mover as primeiras N linhas de saída para finalizar sem usar o arquivo temporário

11

Imagine uma saída de um comando como

44444
55555
11111
22222
33333

como posso arrancar as primeiras N linhas (as duas primeiras no exemplo acima) e anexá-las no final, mas sem usar o arquivo temporário (usando somente pipes)?

11111
22222
33333
44444
55555

Algo ao longo das linhas de | sed -n '3,5p;1,2p' (que obviamente não funciona como sed não se importa com a ordem dos comandos).

    
por Peter Uhnak 20.04.2016 / 11:51

8 respostas

13

Copie essas linhas para o buffer de retenção (depois, exclua-as) e, quando estiver na última linha, anexe o conteúdo do buffer de retenção ao espaço padrão:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

Com gnu sed você pode escrever como

some command | sed '1,NUMBER{H;1h;d;};$G'

Aqui está outra maneira com ol ' ed (ele r ead a saída de some command no buffer de texto e, em seguida, m oves linhas 1,NUMBER após o la $ t one):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

Observe que, como foi apontado, ambos falharão se a saída tiver menos de NUMBER + 1 linhas. Uma abordagem mais sólida seria ( gnu sed syntax):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

este só apaga linhas nesse intervalo, desde que não seja a última linha ( $!d ) - senão substitui o espaço padrão com o conteúdo do buffer de retenção ( g ) e depois q uits (após a impressão o espaço padrão atual).

    
por 20.04.2016 / 11:56
11

Uma abordagem awk :

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

Um benefício sobre a abordagem sed @ don_crissti é que ele ainda funciona (produz as linhas) se a saída tiver n linhas ou menos.

    
por 20.04.2016 / 12:35
6

Eu tenho xclip e com isso isso pode ser feito desta maneira:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

Aqui está sua descrição:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)
    
por 20.04.2016 / 12:06
3

Um caminho Perl:

perl -ne '$.<3?($f.=$_):print;}{print $f'

Ou a mesma coisa escrita de forma menos enigmática:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

Por exemplo:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

Explicação

  • -ne : leia o arquivo de entrada / fluxo linha por linha e aplique o script fornecido por -e para cada linha.
  • $.<3 : $. é o número da linha atual, portanto altere 3 para o número de linhas que você deseja alterar.
  • $.<3?($f.=$_):print; : este é o operador condicional, o formato geral é condition ? case1 : case2 , ele executará case1 se condition for true e case2 se for falso. Aqui, se o número da linha atual for menor que 3, ele anexa a linha atual ( $_ ) à variável $f e, se o número da linha for maior que 3, ela será impressa.
  • }{ print $f : a abreviação }{ perl para END{} . Ele será executado após todas as linhas de entrada terem sido processadas. Neste ponto, teremos coletado todas as linhas que queremos mudar e teremos impresso todas as que queremos deixar em paz, então imprima as linhas salvas como $f .
por 20.04.2016 / 15:38
1

Use POSIX ex . Sim, destina-se à edição de arquivos, mas funcionará em um pipeline.

printf %s\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

Isso pode ter quaisquer comandos arbitrários adicionados no início ou no final do pipeline e funcionará da mesma forma. Melhor ainda, dada a presença de /dev/stdin , é compatível com POSIX.

(não sei se /dev/stdin está especificado em POSIX ou não, mas vejo que está presente no Linux e no Mac OS X.)

Isso tem uma vantagem de legibilidade sobre o uso do espaço de espera de sed - você apenas diz ex "mover essas linhas para o final" e faz isso. (O resto dos comandos significam "print the buffer" e "exit", que são bastante legíveis também.)

NB: O comando ex dado acima falhará se receber menos de duas linhas como entrada.

Leitura adicional:

por 22.04.2016 / 05:54
0

Um pequeno fragmento de python :

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

Passe o nome do arquivo como primeiro argumento e o número de linhas para mover como segundo argumento.

Exemplo:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222
    
por 20.04.2016 / 17:56
0

Se você puder armazenar toda a saída na memória:

data=$(some command)
n=42                  # or whatever
{ tail -n +$((n+1)) <<<"$data"; head -n $n <<<"$data"; } > outputfile
    
por 22.04.2016 / 03:23
0

Aqui está uma outra opção que requer o GNU sed :

(x=$(gsed -u 3q);cat;printf %s\n "$x")

-u torna o GNU sed unbuffered para que o comando sed não consuma mais do que 3 linhas de STDIN. A substituição de comandos remove as linhas vazias, para que o comando não inclua linhas vazias no final da saída se a terceira, terceira e segunda ou terceira, segunda e primeira linha estiverem vazias.

Você também pode fazer algo assim:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

Sem sponge /dev/stdout| , o comando falharia com entradas longas. sponge /dev/stdout também pode ser substituído por tac|tac , embora isso imprima, por exemplo, a\ncb se a entrada for a\nb\nc , em que \n é uma alimentação de linha ou com (x=$(cat);printf %s\n "$x") , embora isso remova linhas vazias da fim da entrada. O comando acima exclui a primeira linha quando o número de linhas da entrada é um ou dois.

    
por 10.06.2016 / 22:39