Comment supprimer les n premières lignes et la dernière ligne d'un fichier à l'aide des commandes shell?

31

J'ai un fichier nommé Element_querycontenant le résultat d'une requête:

SQL> select count (*) from element;

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

SQL> spool off;

Je veux supprimer la 1ère ligne et la dernière ligne en utilisant la commande shell.

pmaipmui
la source
2
Vous feriez probablement mieux de corriger cela dans SQL * Plus; plutôt que de générer un fichier et d'essayer ensuite de supprimer les éléments que vous ne voulez pas, vous pouvez simplement dire à SQL * Plus de ne pas générer ces éléments pour commencer. Une approche est décrite dans la section "Création d'un fichier plat" sur docs.oracle.com/cd/A84870_01/doc/sqlplus.816/a75664/ch44.htm ; une autre approche est décrite sur stackoverflow.com/q/2299375/978917 .
ruakh

Réponses:

48

Utilisation de GNU sed:

sed -i '1d;$d' Element_query

Comment ça marche :

  • -ioption modifier le fichier lui-même. Vous pouvez également supprimer cette option et rediriger la sortie vers un nouveau fichier ou une autre commande si vous le souhaitez.
  • 1dsupprime la première ligne ( 1pour agir uniquement sur la première ligne, dpour la supprimer)
  • $dsupprime la dernière ligne ( $pour agir uniquement sur la dernière ligne, dpour la supprimer)

Aller plus loin :

  • Vous pouvez également supprimer une plage. Par exemple, 1,5dsupprimerait les 5 premières lignes.
  • Vous pouvez également supprimer chaque ligne commençant par l' SQL>utilisation de l'instruction/^SQL> /d
  • Vous pouvez supprimer chaque ligne vide avec /^$/d
  • Enfin, vous pouvez combiner n'importe quelle instruction en les séparant par un point-virgule ( statement1;statement2;satement3;...) ou en les spécifiant séparément sur la ligne de commande ( -e 'statement1' -e 'statement 2' ...)
user43791
la source
Si sa 3ème ligne à supprimer ... alors je dois utiliser 3d à la place de 1d? si sa 3ème ligne de la dernière à supprimer ... alors quelle sera la commande?
pmaipmui
Comment supprimer la 3ème ligne de la dernière en utilisant les commandes shell?
pmaipmui
@Nainita Vous pouvez spécifier une plage ( 1,3dsupprimera les trois premières lignes) mais c'est un peu plus difficile pour la fin. Selon ce que vous voulez, vous pourriez être mieux en utilisant ceci: sed -i '/^SQL> /d' Element_querypour supprimer les lignes qui commencent SQL> peu importe où elles se trouvent dans le fichier.
user43791
@Nainita - voir ma réponse ici pour les dénombrements arbitraires - il propose deux solutions pour supprimer les lignes de dénombrement par rapport à la fin du fichier. L'une est une ligne sedunique - qui fonctionnera pour supprimer le nombre de lignes arbitraires de la tête et de la queue d'un fichier, Mieux cependant, tant que l'entrée est un fichier normal, il suffit de regrouper une seule entrée sur deux headprocessus - c'est la moyen le plus rapide de le faire habituellement.
mikeserv
J'avais l'habitude sed -i '1d' table-backup.sqlde supprimer la première ligne du fichier texte sql
David Thomas
8

tête; tête

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

Avec ce qui précède, vous pouvez spécifier le premier nombre de lignes à retirer de la tête de la sortie avec la première headcommande, et le nombre de lignes à écrire outfileavec la seconde. Il le fera également généralement plus rapidement que sed- en particulier lorsque l'entrée est volumineuse - bien qu'il nécessite deux appels. Où sedcertainement devrait être préféré cependant, est dans le cas qui <infileest pas régulièrement, lseekable fichier - car cela généralement pas fonctionner comme prévu dans ce cas, mais sedpeut gérer toutes les modifications de sortie dans un seul processus scénarisé.

Avec un GNU, headvous pouvez également utiliser la -forme négative pour [num]dans la deuxième commande. Dans ce cas, la commande suivante supprimera la première et la dernière ligne de l'entrée:

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

OU avec un POSIX sed:

Supposons, par exemple, que je lisais une entrée de 20 lignes et que je voulais supprimer les 3 premiers et les 7 derniers. Si je décidais de le faire avec sed, je le ferais avec un tampon de queue. Je voudrais d'abord additionner trois et sept pour un nombre total de bandes de dix, puis faire:

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

C'est un exemple qui supprime les 3 et 7 dernières lignes de l'entrée. L'idée est que vous pouvez mettre en mémoire tampon autant de lignes que vous souhaitez supprimer de la queue de l'entrée dans l'espace de motif sur une pile, mais seulement Préimprimer la première de celles-ci pour chaque ligne tirée.

  • On lines 1,10 sed Pn'imprime rien car pour chacun d'entre eux, il empile les entrées ligne par ligne dans un espace de motif dans une bboucle ranch.
  • Sur la 3ème ligne, la totalité de sedla pile est dterminée - et donc les 3 premières lignes sont supprimées de la sortie en un seul coup.
  • Lorsqu'il sedatteint la $dernière ligne d'entrée et tente d'extraire l' Nextension, il frappe EOF et arrête complètement le traitement. Mais à ce moment-là, l'espace de motif contient toutes les lignes 14,20- dont aucune n'a encore été Pimprimée, et ne le sont jamais.
  • Sur toutes les autres lignes, sed Pne s'imprime que jusqu'à la première ligne \nélectronique apparaissant dans l'espace de motif, et les Dsupprime avant de commencer un nouveau cycle avec ce qui reste - ou les 6 lignes d'entrée suivantes. La 7ème ligne est à nouveau ajoutée à la pile avec la Ncommande ext dans le nouveau cycle.

Et donc, de seqla sortie de (qui est 20 lignes numérotées séquentiellement) , sedimprime uniquement:

4
5
6
7
8
9
10
11
12
13

Cela devient problématique lorsque le nombre de lignes que vous souhaitez supprimer de la queue d'entrée est important, car sedles performances de sont directement proportionnelles à la taille de son espace de motif. Pourtant, c'est une solution viable dans de nombreux cas - et POSIX spécifie un sedespace de modèle pour gérer au moins 4 Ko avant de se casser.

mikeserv
la source
1
gnu tailprend également en charge la tail -n+<num>syntaxe étendue signifiant «commencer à partir de la ligne <num>»
UloPe
4

Je ne vais pas vous dire comment supprimer un certain nombre de lignes. Je vais attaquer le problème de cette façon:

grep -v '#SQL>' Element_query >outfile

Au lieu de compter les lignes, il élimine les commandes SQL en reconnaissant les invites. Cette solution peut ensuite être généralisée pour d'autres fichiers de sortie de sessions SQL avec plus de commandes que deux.

Monty Harder
la source
Je l'aime. Je ne sais pas grand chose sur SQL - mais n'y a-t-il aucune chance que les invites se produisent en tête de ses lignes de sortie autrement?
mikeserv
4

edest «l'éditeur de texte standard» et devrait être disponible sur les systèmes qui n'ont pas GNU sed. Il a été initialement conçu comme un éditeur de texte, mais il est bien adapté aux scripts.

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

1dsupprime la première ligne du fichier, $d(citée pour que le shell ne pense pas que ce soit une variable) supprime la dernière ligne, wécrit le fichier et qquitte ed. printfest ici utilisé pour formater les commandes de ed- chacune doit être suivie d'une nouvelle ligne; il existe bien sûr d'autres moyens d'y parvenir.

evilsoup
la source
3

Il existe plusieurs façons de supprimer les lignes de début et de fin d'un fichier.

Vous pouvez l'utiliser awkcar il gère à la fois la correspondance de motifs et le comptage de lignes,

#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};'

Vous pouvez utiliser grep -vpour exclure les lignes que vous ne voulez pas par motif, et vous pouvez faire correspondre plusieurs motifs en utilisant l' -Eoption,

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

Vous pouvez utiliser headet tailpour découper des nombres spécifiques de lignes,

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

Vous pouvez utiliser vi/vimet supprimer les première et dernière ligne (s),

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

vous pouvez utiliser un script perl, sauter la première ligne, enregistrer chaque ligne, imprimer lorsque vous obtenez une ligne suivante,

#left as exercise for the reader :-)
ChuckCottrill
la source
1
Pour les heads, vous n'avez pas vraiment besoin du tuyau, et, en fait, il vaut mieux ne pas l'utiliser du tout si vous pouvez vous en tirer. Lorsque vous le faites head | head, bien que les deux processus puissent s'exécuter simultanément, ils traitent également pratiquement tous les mêmes données de manière redondante. Si vous le faites à la place, { head >dump; head >save; } <invous ne sautez que par décalage - le premier lit 10 lignes >dumpet le second lit les 10 lignes suivantes>save .
mikeserv
3

Vous seriez bien mieux servi en supprimant les commandes SQL. Vous pouvez le faire de deux manières:

  1. Si vous êtes absolument sûr que la séquence " SQL>" ne se produit nulle part ailleurs dans la sortie,

    grep -v -F 'SQL> ' < infile > outfile
  2. Si vous n'êtes pas aussi sûr,

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

La deuxième version est plus lente mais plus précise: elle ignorera les lignes commençant exactement par "SQL>" et se terminant par un point-virgule, qui semblent décrire les lignes que vous souhaitez éliminer.

Cependant, il serait préférable de ne pas mettre cette sortie supplémentaire dans le fichier pour commencer. La plupart des systèmes SQL ont cette possibilité. Je ne connais pas trop Oracle, mais cette réponse pourrait peut-être être utile.

LSerni
la source
3

Vous pouvez sélectionner les lignes entre une plage awk(cela suppose que vous connaissez le nombre de lignes):

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

Ou en Perl:

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

Si vous ne savez pas combien de lignes il y a, vous pouvez le calculer à la volée en utilisant grep(notez que cela ne comptera pas les lignes vides, utilisez-les grep -c '' filepour les compter également):

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

Essayez cette solution:

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

Personnalisation

Vous pouvez facilement l'adapter pour supprimer les n premières lignes en changeant le +2of tail;
ou pour supprimer les n dernières lignes en changeant le -1de head.

Gabrer
la source
Cette solution est incorrecte car elle imprime la première ligne.
xhienne
1
@xhienne Désolé, c'était une erreur. J'ai écrit 1 au lieu de 2 comme paramètre de "queue". Maintenant ça marche, merci! :)
Gabrer
1

En utilisant awk:

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile: redirige le contenu de inputfileversawk « sstdin
  • > outputfile: Réoriente le contenu de awk« sstdout versoutputfile
  • NR>1: exécute les actions suivantes uniquement si le numéro de l'enregistrement en cours de traitement est supérieur à 1
  • {print r}: imprime le contenu de la variable r
  • {r=$0}: affecte le contenu de l'enregistrement en cours de traitement à la variable r

Ainsi, lors de la première exécution du awkscript, le premier bloc d'actions n'est pas exécuté, tandis que le deuxième bloc d'actions est exécuté et le contenu de l'enregistrement est affecté à la variable r; à la deuxième exécution, le premier bloc d'actions est exécuté, et le contenu de la variable rest imprimé (donc l'enregistrement précédent est imprimé); cela a pour effet d'imprimer chaque ligne traitée mais la première et la dernière.

kos
la source
Vous n'excluez pas la première ligne. À NR == 2, vous imprimez la première ligne d'entrée qui a été stockée dans r.
xhienne