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

31

Eu tenho um arquivo chamado Element_querycontendo 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.

pmaipmui
fonte
2
Provavelmente, é melhor corrigir isso dentro do SQL * Plus; em vez de gerar um arquivo e depois tentar aparar as coisas que você não deseja, basta dizer ao SQL * Plus para não gerar essas coisas para começar. Uma abordagem é descrita na seção "Criando um arquivo simples " em docs.oracle.com/cd/A84870_01/doc/sqlplus.816/a75664/ch44.htm ; outra abordagem é descrita em stackoverflow.com/q/2299375/978917 .
Ruakh

Respostas:

48

Usando o GNU sed:

sed -i '1d;$d' Element_query

Como funciona :

  • -iopção editar o próprio arquivo. Você também pode remover essa opção e redirecionar a saída para um novo arquivo ou outro comando, se desejar.
  • 1dexclui a primeira linha ( 1para agir apenas na primeira linha, dpara excluí-la)
  • $dexclui a última linha ( $para atuar apenas na última linha, dpara excluí-la)

Indo além :

  • Você também pode excluir um intervalo. Por exemplo, 1,5dexcluiria as 5 primeiras linhas.
  • Você também pode excluir todas as linhas que começam com o SQL>uso da instrução/^SQL> /d
  • Você pode excluir todas as linhas em branco com /^$/d
  • Por fim, você pode combinar qualquer uma das instruções separando-as com ponto-e-vírgula ( statement1;statement2;satement3;...) ou especificando-as separadamente na linha de comando ( -e 'statement1' -e 'statement 2' ...)
user43791
fonte
Se a sua terceira linha para excluir ... então eu tenho que usar 3d no lugar de 1d? se a sua terceira linha for a última a excluir ... então qual será o comando?
Pmaipmui
Como excluir a terceira linha da última usando comandos shell?
Pmaipmui
@Nainita Você pode especificar um intervalo ( 1,3dexcluirá as três primeiras linhas), mas é um pouco mais difícil para o final. Dependendo do que você deseja, é melhor usar isso: sed -i '/^SQL> /d' Element_querypara excluir as linhas que começam SQL> sem importar onde estão no arquivo.
user43791
@Nainita - veja minha resposta aqui para contagens arbitrárias de cauda - ele oferece duas soluções para eliminar linhas de contagem em relação ao final do arquivo. Um deles é o sedone-liner - que funcionará para remover contagens arbitrárias de linhas do cabeçalho e do final de um arquivo. Melhor ainda, desde que a entrada seja um arquivo regular, seja apenas para agrupar uma única entrada em dois headprocessos - é o maneira mais rápida de fazer isso normalmente.
mikeserv
Eu costumava sed -i '1d' table-backup.sqlexcluir a primeira linha do arquivo de texto sql
David Thomas
8

cabeça; cabeça

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

Com o exposto, você pode especificar o primeiro número de linhas a serem removidas da cabeça da saída com o primeiro headcomando e o número de linhas a serem gravadas outfilecom o segundo. Ele também costuma fazer isso mais rápido do que sed- especialmente quando a entrada é grande - apesar de exigir duas invocações. Onde seddefinitivamente deve ser preferido, porém, é no caso em que <infileé não um regular, lseekable arquivo - porque isso irá normalmente não funcionar como o esperado nesse caso, mas sedpode lidar com todas as modificações de saída em um único processo, roteirizado.

Com um GNU, headvocê também pode usar a -forma negativa [num]no segundo comando. Nesse caso, o seguinte comando removerá a primeira e a última linha da entrada:

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

OU com um POSIX sed:

Digamos, por exemplo, eu estava lendo uma entrada de 20 linhas e queria retirar as 3 e as 7 primeiras. Se resolvesse fazer isso com / sed, faria 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 3 primeiras e as 7 últimas linhas da entrada. A idéia é que você pode armazenar em buffer quantas linhas desejar retirar da cauda da entrada no espaço do padrão em uma pilha, mas apenas Pcriar a primeira delas para cada linha puxada.

  • Nas linhas 1,10 sed Pnão há nada porque, para cada uma delas, está empilhando a entrada no espaço do padrão linha por linha em um bloop de rancho.
  • Na terceira linha, toda seda pilha é deliminada - e, assim, as três primeiras linhas são retiradas da saída de uma só vez.
  • Quando sedatinge a $última linha de entrada e tenta puxar o Next, atinge o EOF e para o processamento completamente. Mas naquele tempo, o espaço do padrão contém todas as linhas 14,20- nenhuma das quais ainda foi Pcriada e nunca é.
  • Em todas as outras linhas, sed Papenas as linhas de ew ocorrem \nno espaço do padrão e as Dexclui antes de iniciar um novo ciclo com o que resta - ou as próximas 6 linhas de entrada. A sétima linha é anexada novamente à pilha com o Ncomando ext no novo ciclo.

E assim, da seqsaída de (que é 20 linhas numeradas seqüencialmente) , sedapenas imprime:

4
5
6
7
8
9
10
11
12
13

Isso se torna problemático quando o número de linhas que você deseja extrair da cauda da entrada é grande - porque sedo desempenho é diretamente proporcional ao tamanho do espaço do padrão. Ainda assim, porém, é uma solução viável em muitos casos - e o POSIX especifica um sedespaço padrão para lidar com pelo menos 4kb antes de ser eliminado.

mikeserv
fonte
1
O GNU tailtambém suporta a tail -n+<num>sintaxe estendida, que significa "iniciar da linha <num>"
UloPe em
4

Não vou responder como excluir várias linhas. 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.

Monty Harder
fonte
Eu gosto disso. Eu não sei muito sobre SQL - mas não há nenhuma chance de os prompts ocorrerem no início de suas linhas de saída?
mikeserv
4

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

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

1dexclui a primeira linha do arquivo $d(citada para que o shell não pense que seja uma variável) exclui a última linha, wgrava o arquivo e qsai ed. printfaqui é usado para formatar os comandos para ed- cada um deve ser seguido por uma nova linha; é claro que existem outras maneiras de conseguir isso.

evilsoup
fonte
3

Existem várias maneiras de remover as linhas inicial e final de um arquivo.

Você pode usá- awklo, pois lida com a correspondência de padrões e a 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 -vpara excluir linhas que não deseja por padrão e pode combinar vários padrões usando a -Eopção

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

Você pode usar heade tailaparar 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/vime 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 a próxima linha,

#left as exercise for the reader :-)
ChuckCottrill
fonte
1
Para os heads, você realmente não precisa do cachimbo e, de fato, é melhor não usá-lo, se você puder se safar. Quando você faz isso head | head- enquanto os dois processos podem ser executados simultaneamente, eles também estão processando praticamente todos os mesmos dados de forma redundante. Se você fizer { head >dump; head >save; } <inisso, pule apenas por deslocamento - o primeiro lê 10 linhas >dumpe o segundo lê as próximas 10 linhas >save.
mikeserv
3

Você seria muito melhor servido cortando os comandos SQL. Você pode fazer isso de duas maneiras:

  1. Se você tiver certeza absoluta de que a sequência " SQL>" não ocorre em nenhum outro lugar na saída,

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

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

A segunda versão é mais lenta, porém mais precisa: ela ignora as linhas que começam exatamente com "SQL>" e terminam em ponto e vírgula, que parecem descrever as linhas que você deseja 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. Não estou muito familiarizado com a Oracle, mas talvez essa resposta possa ser útil.

LSerni
fonte
3

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

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

Ou no Perl:

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

Se você não souber quantas linhas existem, poderá calculá-lo em tempo real usando grep(observe que isso não contará linhas em branco, use grep -c '' file-as também):

awk -vm="$(grep -c . file2.txt)" 'NR>1 && NR<m' file2.txt
terdon
fonte
3

Experimente esta solução:

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

Costumização

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

Gabrer
fonte
Esta solução está incorreta, pois imprime a primeira linha.
Xhienne 29/03
1
@xhienne Desculpe, foi um erro. Eu escrevi 1 em vez de 2 como o parâmetro "tail". Agora funciona, obrigado! :)
Gabrer
1

Usando awk:

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile: Redireciona o conteúdo inputfilede awk'sstdin
  • > outputfile: redireciona o conteúdo de awk's stdoutparaoutputfile
  • 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 que está sendo processado à variável r

Portanto, na primeira execução do awkscript, 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 (para que o registro anterior seja impresso); isso tem o efeito de imprimir cada linha processada, mas a primeira e a última.

kos
fonte
Você não está excluindo a primeira linha. Em NR == 2, você imprime a primeira linha de entrada armazenada em r.
Xhienne 29/03