Comment obtenir un comportement inverse pour `tail` et` head`?

55

Y at-il un moyen de head/ tailun document et obtenir la sortie inversée; parce que vous ne savez pas combien de lignes il y a dans un document?

C'est-à-dire que je veux juste tout obtenir sauf les 2 premières lignes de l' foo.txtannexe d'un autre document.

chrisjlee
la source

Réponses:

57

Vous pouvez utiliser ceci pour effacer les deux premières lignes:

tail -n +3 foo.txt

et ceci pour dépouiller les deux dernières lignes:

head -n -2 foo.txt

(en supposant que le fichier se termine par \npour ce dernier)


Tout comme pour l'utilisation standard de tailet headces opérations ne sont pas destructives. Utilisez >out.txtsi vous voulez rediriger la sortie vers un nouveau fichier:

tail -n +3 foo.txt >out.txt

Si le cas out.txtexiste déjà, il écrasera ce fichier. Utilisez >>out.txtplutôt que >out.txtsi vous préférez avoir la sortie ajoutée out.txt.

Stéphane Gimenez
la source
3
re « tête, lorsque se termine fichier avec \n» .. Il fonctionne pour tous les entiers négatifs autres que ce -n -0qui retours rien du tout , tout comme -n 0serait ( en utilisant: la tête (coreutils GNU) 7.4) ... Cependant , quand une fuite \nest présente, -n -0ne imprime comme on pouvait s'y attendre de la -, à savoir. il affiche le fichier entier ... Donc, il fonctionne pour toutes les valeurs négatives non nulles .. mais -0échoue s'il n'y a pas de fin\n
Peter.O
@fred: Etrange en effet… (idem avec 8.12 ici).
Stéphane Gimenez
Cette opération est-elle destructive? Comme je veux le copier l'inverse des deux premières lignes du document à un autre?
chrisjlee
@Chris: Non, ils affichent simplement le résultat sur leur "sortie standard", généralement connectée au terminal. J'ai ajouté quelques détails sur la façon de rediriger la sortie vers certains fichiers.
Stéphane Gimenez
7
head -n -2n'est pas compatible POSIX .
l0b0
9

Si vous voulez toutes les lignes sauf les premières N-1, appelez tailavec le nombre de lignes +N. (Le nombre correspond au numéro de la première ligne que vous souhaitez conserver, commençant à 1, c'est-à-dire que +1 signifie que vous commencez en haut, +2 signifie que vous sautez une ligne, etc.).

tail -n +3 foo.txt >>other-document

Il n'y a pas de moyen facile et portable de sauter les N dernières lignes. GNU headpermet head -n +Nen contrepartie de tail -n +N. Sinon, si vous avez tac(par exemple, GNU ou Busybox), vous pouvez le combiner avec tail:

tac | tail -n +3 | tac

De manière portable, vous pouvez utiliser un filtre awk (non testé):

awk -vskip=2 '{
    lines[NR] = $0;
    if (NR > skip) print lines[NR-skip];
    delete lines[NR-skip];
}'

Si vous souhaitez supprimer les dernières lignes d'un fichier volumineux, vous pouvez déterminer le décalage en octets de la pièce à tronquer, puis effectuer la troncature avec dd.

total=$(wc -c < /file/to/truncate)
chop=$(tail -n 42 /file/to/truncate | wc -c)
dd if=/dev/null of=/file/to/truncate seek=1 bs="$((total-chop))"

Vous ne pouvez pas tronquer un fichier en place au début. Toutefois, si vous devez supprimer les premières lignes d'un fichier volumineux, vous pouvez déplacer le contenu .

Gilles, arrête de faire le mal
la source
Sur certains systèmes (comme Linux moderne), vous pouvez tronquer (réduire) un fichier en place au début, mais en général uniquement par une quantité multiple de la taille du bloc FS (ce qui n’est donc pas vraiment utile dans ce cas).
Stéphane Chazelas
3

Depuis la tailpage de manuel (GNU tail, c’est-à-dire):

-n, --lines=K
   output the last K lines, instead of the last 10; or use -n +K to
   output lines starting with the Kth

Ainsi, ce qui suit devrait ajouter tous , mais les 2 premières lignes de somefile.txtà anotherfile.txt:

tail --lines=+3 somefile.txt >> anotherfile.txt
Steven Monday
la source
3

Pour supprimer les n premières lignes, GNU sed peut être utilisé. Par exemple si n = 2

sed -n '1,2!p' input-file

La !moyenne "exclut cet intervalle". Comme vous pouvez l’imaginer, un résultat plus compliqué peut être obtenu, par exemple

sed -n '3,5p;7p'

cela montrera la ligne 3,4,5,7. Plus de puissance provient de l'utilisation d'expressions régulières au lieu d'adresses.

La limite est que les numéros de lignes doivent être connus à l'avance.

enzotib
la source
1
Pourquoi pas juste sed 1,2d? Plus simple est généralement mieux. En outre, rien dans vos exemples n’est spécifique à GNU Sed; vos commandes utilisent toutes les fonctionnalités POSIX standard de Sed .
Wildcard
1

Vous pouvez utiliser diffpour comparer la sortie de head/ tailau fichier d'origine, puis supprimer ce qui est identique, obtenant ainsi l'inverse.

diff --unchanged-group-format='' foo.txt <(head -2 foo.txt)
sokoban
la source
1

Tandis que la tail -n +4sortie du fichier à partir de la 4ème ligne (toutes les lignes sauf les 3 premières) est standard et portable, sa headcontrepartie ( head -n -3toutes les lignes sauf les 3 dernières) ne l’est pas.

Portable, vous feriez:

sed '$d' | sed '$d' | sed '$d'

Ou:

sed -ne :1 -e '1,3{N;b1' -e '}' -e 'P;N;D'

(Méfiez-vous que sur certains systèmes où l’ sedespace de modèle est de taille limitée, les valeurs ne sont pas élevées n)

Ou:

awk 'NR>3 {print l[NR%3]}; {l[NR%3]=$0}'
Stéphane Chazelas
la source
1
{   head -n2 >/dev/null
    cat  >> other_document
}   <infile

Si <infileest un lseek()fichier normal, alors oui, certainement, n'hésitez pas. Ce qui précède est une construction entièrement prise en charge par POSIXly.

Mikeserv
la source
0

Mon approche est similaire à celle de Gilles, mais je ne fais que renverser le fichier avec cat et le diriger avec la commande head.

tac -r thefile.txt | head thisfile.txt (remplace les fichiers)

Abe
la source
0

J'espère que j'ai bien compris votre besoin.

Vous avez plusieurs façons de compléter votre demande:

tail -n$(expr $(cat /etc/passwd|wc -l) - 2) /etc/passwd

/ etc / passwd est votre fichier

La 2ème solution peut être utile si vous avez un gros fichier:

my1stline=$(head -n1 /etc/passwd)
my2ndline=$(head -n2 /etc/passwd|grep -v "$my1stline")
cat /etc/passwd |grep -Ev "$my1stline|$my2ndline"
Mathieu Coavoux
la source
0

Solution pour BSD (macOS):

Supprimer les 2 premières lignes:

tail -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

Supprimer les 2 dernières lignes:

head -n $( echo "$(cat foo.txt | wc -l)-2" | bc )

... pas très élégant mais fait le travail!

spectre
la source