web-dev-qa-db-fra.com

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

J'ai un fichier nommé Element_query contenant 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 à l'aide de la commande Shell.

38
pmaipmui

Utilisation de GNU sed:

sed -i '1d;$d' Element_query

Comment ça marche:

  • -i option modifie 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.
  • 1d supprime la première ligne (1 pour n'agir que sur la première ligne, d pour la supprimer)
  • $d supprime la dernière ligne ($ pour n'agir que sur la dernière ligne, d pour la supprimer)

Pour aller plus loin:

  • Vous pouvez également supprimer une plage. Par exemple, 1,5d supprimerait les 5 premières lignes.
  • Vous pouvez également supprimer toutes les lignes commençant par SQL> en utilisant 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' ...)
55
user43791

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 commande head, et le nombre de lignes à écrire outfile avec le second. Il le fera également plus rapidement que sed - en particulier lorsque l'entrée est large - bien qu'il nécessite deux appels. Où sed définitivement devrait être préféré cependant, c'est dans le cas où <infile est pas un fichier régulier, lseekable - car cela fonctionnera généralement pas comme prévu dans ce cas, mais sed peut gérer toutes les modifications de sortie dans un seul processus scripté.

Avec un GNU head vous pouvez utiliser le - forme négative pour [num] dans la deuxième commande également. 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 Print la première de ces lignes pour chaque ligne tirée.

  • Sur les lignes 1,10sedP n'imprime rien car pour chacun d'entre eux, il empile les entrées ligne par ligne dans un espace modèle dans une boucle branch.
  • Sur la 3ème ligne, toute la pile de sed est deleted - et donc les 3 premières lignes sont supprimées de la sortie d'un seul coup.
  • Lorsque sed atteint le $la dernière ligne d'entrée et tente d'extraire le Next il frappe EOF et arrête complètement le traitement. Mais à ce moment-là, l'espace de modèle contient toutes les lignes 14,20 - dont aucun n'a encore été Pimprimé, et ne le sont jamais.
  • Sur toutes les autres lignes, sedPimprime uniquement jusqu'à la première occurrence \newline dans l'espace modèle, et Deletes même 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 commande Next dans le nouveau cycle.

Et donc, de la sortie de seq(qui est 20 lignes numérotées séquentiellement), sed affiche 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 les performances de sed sont directement proportionnelles à la taille de son espace de motif. Néanmoins, c'est une solution viable dans de nombreux cas - et POSIX spécifie un espace de modèle sed pour gérer au moins 4 Ko avant de se casser.

8
mikeserv

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 +2 de (tail;
ou pour supprimer les n dernières lignes en changeant le -1 de head.

5
Gabrer

Vous pouvez sélectionner les lignes entre une plage dans 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 grep -c '' file pour les compter également):

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

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.

4
Monty Harder

ed est "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

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

4
evilsoup

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

  1. Si vous êtes absolument sûr que la séquence "SQL> "ne pas se produit n'importe où 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 un moyen de le faire. Je ne connais pas trop Oracle, mais peut-être cette réponse pourrait être utile.

3
LSerni

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

Vous pouvez utiliser awk car 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 -v pour exclure les lignes que vous ne voulez pas par motif, et vous pouvez faire correspondre plusieurs motifs en utilisant le -E option,

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

Vous pouvez utiliser head et tail pour 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/vim, et supprimez la première et la 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 :-)
3
ChuckCottrill

Utilisation de awk:

< inputfile awk 'NR>1 {print r}; {r=$0}' > outputfile
  • < inputfile: redirige le contenu de inputfile vers awkstdin
  • > outputfile: redirige le contenu de awkstdout vers outputfile
  • 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}: affiche 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 script awk, 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; lors de la deuxième exécution, le premier bloc d'actions est exécuté et le contenu de la variable r est 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.

1
kos