Como eu excluo as primeiras n linhas e a última linha de um arquivo usando comandos shell?

19

Eu tenho um arquivo chamado Element_query contendo o resultado de uma consulta:

SQL> select count (*) from element;

[Output of the query which I want to keep in my file]

SQL> spool off;

Eu quero excluir a primeira linha e a última linha usando o comando shell.

    
por pmaipmui 11.06.2015 / 20:25

9 respostas

31

Usando o GNU sed :

sed -i '1d;$d' Element_query

Como funciona:

  • -i option edita o arquivo em si. Você também pode remover essa opção e redirecionar a saída para um novo arquivo ou outro comando, se desejar.
  • 1d exclui a primeira linha ( 1 para agir somente na primeira linha, d para excluí-la)
  • $d exclui a última linha ( $ para atuar somente na última linha, d para excluí-la)

Indo mais longe:

  • Você também pode excluir um intervalo. Por exemplo, 1,5d excluiria as 5 primeiras linhas.
  • Você também pode excluir todas as linhas que começam com SQL> usando a instrução /^SQL> /d
  • Você pode excluir todas as linhas em branco com /^$/d
  • Finalmente, você pode combinar qualquer uma das instruções, separando-as com um ponto e vírgula ( statement1;statement2;satement3;... ) ou especificando-as separadamente na linha de comando ( -e 'statement1' -e 'statement 2' ... )
por 11.06.2015 / 20:29
7

cabeça; cabeça

{   head -n[num] >/dev/null
    head -n[num]
}  <infile >outfile

Com o exposto acima, você pode especificar o primeiro número de linhas a partir da cabeça da saída com o primeiro comando head e o número de linhas a serem gravadas em outfile com o segundo. Ele normalmente também faz isso mais rápido que sed - especialmente quando a entrada é grande - apesar de exigir duas invocações. Onde sed definitivamente deve ser preferível, no caso em que <infile não é um arquivo regular, lseekable - porque isso normalmente não funcionam como pretendido nesse caso, mas sed pode manipular todas as modificações de saída em um único processo com script.

Com um% GNUhead você pode usar o formulário - negativo para [num] no segundo comando também. Nesse caso, o comando a seguir tira as primeiras e últimas linhas da entrada:

{   head -n1 >/dev/null
    head -n-1
}  <infile >outfile

OU com um POSIX sed :

Digamos, por exemplo, que eu estava lendo uma entrada de 20 linhas e queria remover as 3 primeiras e as 7 últimas. Se eu resolvesse fazer isso com sed , faria isso com um buffer de cauda. Eu adicionaria primeiro três e sete para uma contagem total de dez e depois faria:

seq 20 | sed -ne:n -e '3d;N;1,10bn' -eP\;D

Esse é um exemplo que retira as primeiras 3 e últimas 7 linhas da entrada. A ideia é que você possa armazenar em buffer tantas linhas quanto desejar retirar da cauda de entrada no espaço de padrão em uma pilha, mas apenas P rint o primeiro para cada linha extraída.

  • Nas linhas 1,10 sed P rints nada porque, para cada uma delas, é empilhamento de entrada no espaço de padrão linha por linha em um loop de rancho b .
  • Na terceira linha, toda a pilha de sed é d eleted - e, portanto, as primeiras 3 linhas são removidas da saída de uma só vez.
  • Quando sed alcança a última linha de entrada $ e tenta extrair o N ext, ele atinge o EOF e pára completamente o processamento. Mas nesse momento, o espaço padrão contém todas as linhas 14,20 - nenhuma delas foi P rinted e nunca é.
  • Em todas as outras linhas sed P rints até o primeiro \n ewline no espaço de padrões, e D é igual antes de iniciar um novo ciclo com o que resta - ou as próximas 6 linhas de entrada . A 7ª linha é anexada novamente à pilha com o comando N ext no novo ciclo.

E assim, da saída de seq (que é 20 linhas numeradas sequencialmente) , sed apenas imprime:

4
5
6
7
8
9
10
11
12
13

Isso pode ser problemático quando o número de linhas que você deseja remover da cauda de entrada é grande - porque o desempenho de sed é diretamente proporcional ao tamanho de seu espaço de padrão. Ainda assim, no entanto, é uma solução viável em muitos casos - e o POSIX especifica um espaço de padrão sed para lidar com pelo menos 4 kb antes de rebentar.

    
por 11.06.2015 / 20:47
4

Não vou responder como excluir várias linhas. Eu vou atacar o problema desta maneira:

grep -v '#SQL>' Element_query >outfile

Em vez de contar linhas, elimina os comandos SQL reconhecendo os prompts. Essa solução pode ser generalizada para outros arquivos de saída de sessões SQL com mais comandos do que apenas dois.

    
por 11.06.2015 / 21:34
4

ed é 'o editor de texto padrão' e deve estar disponível em sistemas que não possuem o GNU sed . Ele foi originalmente projetado como um editor de texto, mas é bem adequado para scripts.

printf '%s\n' 1d '$d' w q | ed Element_query

1d exclui a primeira linha do arquivo, $d (citado para que o shell não ache que é uma variável) exclui a última linha, w grava o arquivo e q sai ed . printf é usado aqui para formatar os comandos para ed - cada um deve ser seguido por uma nova linha; há, claro, outras maneiras de conseguir isso.

    
por 11.06.2015 / 22:51
3

Existem várias maneiras de remover linha (s) inicial e final (es) de um arquivo.

Você pode usar awk ao lidar com correspondência de padrões e contagem de linhas,

#you need to know length to skip last line, assume we have 100 lines
awk 'NR>1 && NR<100 {print $0};' < inputfile
#or
awk '/SQL/ {next}; {print $0;};' < inputfile |awk 'NR>1&& NR<10 {print $0};'

Você pode usar grep -v para excluir linhas que não deseja por padrão e pode combinar vários padrões usando a opção -E ,

grep -v -E "SQL>" < inputfile > outputfile

Você pode usar head e tail para aparar contagens específicas de linhas,

lines=$(('wc -l < inputfile'-2)) #how many lines in file, less 2
head -n-1 < inputfile | head -n$lines > outputfile
#or
tail -n+2 < inputfile | head -n$lines > outputfile

Você pode usar vi/vim e excluir a primeira e a última linha (s),

vi inputfile
:1
dd
:$
dd
:w! outputfile
:x

você pode usar um script perl, pular a primeira linha, salvar cada linha, imprimir quando chegar na próxima linha,

#left as exercise for the reader :-)
    
por 12.06.2015 / 04:43
3

Você seria muito melhor atendido pelo cortar os comandos SQL. Você pode fazer isso de duas maneiras:

  1. Se você está absolutamente certo de que a sequência " SQL> " não não ocorre em nenhum outro lugar na saída,

    grep -v -F 'SQL> ' < infile > outfile
    
  2. Se você não tiver certeza,

    grep -v '^SQL> .*;$' < infile > outfile
    

A segunda versão é mais lenta, mas mais precisa: ela ignorará as linhas começando exatamente com "SQL >" e terminando com ponto e vírgula, que parecem descrever as linhas que você quer eliminar.

No entanto, seria melhor não colocar essa saída extra no arquivo para começar. A maioria dos sistemas SQL tem alguma maneira de fazer isso. Eu não sou muito familiarizado com o Oracle, mas talvez esta resposta pode ser útil.

    
por 11.06.2015 / 23:04
3

Você pode selecionar as linhas entre um intervalo em awk (isso pressupõe que você saiba quantas linhas existem):

awk 'NR>1 && NR < 3' file

Ou em Perl:

perl -ne 'print if $.>1 && $.<3' file

Se você não sabe quantas linhas existem, você pode calculá-las rapidamente usando grep (observe que isso não contará linhas em branco, use grep -c '' file para contá-las também):

awk -vm="$(grep -c . file2.txt)" 'NR>1 && NR<m' file2.txt
    
por 12.06.2015 / 12:55
2

Experimente esta solução:

tail -n +2 name_of_file | head -n-1

Personalização

Você pode adaptá-lo facilmente para excluir as primeiras linhas que alteram o +2 de tail ; ou para excluir as últimas n linhas alterando o -1 de head .

    
por 29.03.2017 / 01:24
1

Usando awk :

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile : redireciona o conteúdo de inputfile para awk ' stdin
  • > outputfile : redireciona o conteúdo de awk stdout para outputfile
  • NR>1 : executa as seguintes ações apenas se o número do registro que está sendo processado for maior que 1
  • {print r} : imprime o conteúdo da variável r
  • {r=$0} : atribui o conteúdo do registro sendo processado à variável r

Portanto, na primeira execução do script awk , o primeiro bloco de ações não é executado, enquanto o segundo bloco de ações é executado e o conteúdo do registro é atribuído à variável r ; na segunda execução, o primeiro bloco de ações é executado e o conteúdo da variável r é impresso (assim, o registro anterior é impresso); isso tem o efeito de imprimir cada linha processada, mas a primeira e a última.

    
por 11.06.2015 / 23:15