web-dev-qa-db-fra.com

unix - tête ET queue de fichier

Disons que vous avez un fichier txt, quelle est la commande pour afficher simultanément les 10 premières lignes et les 10 dernières lignes du fichier?

c'est-à-dire si le fichier a 200 lignes, visualisez les lignes 1-10 et 190-200 en une fois. 

109
toop

Vous pouvez simplement:

(head; tail) < file.txt

Et si vous devez utiliser des tuyaux pour une raison quelconque, procédez comme suit:

cat file.txt | (head; tail)

Remarque: imprimera les lignes dupliquées si le nombre de lignes dans le fichier file.txt est inférieur au nombre de lignes par défaut en tête et de lignes par défaut.

176
Aleksandra Zalcman

ed est le standard text editor

$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
17
kev

Pour un flux pur (par exemple, une sortie d'une commande), vous pouvez utiliser 'tee' pour créer le flux et envoyer un flux à head et un à tail. Cela nécessite l’utilisation de la fonction '> (liste)' de bash (+/dev/fd/N):

( COMMAND | tee /dev/fd/3 | head ) 3> >( tail )

ou en utilisant/dev/fd/N (ou/dev/stderr) plus des sous-shell avec une redirection compliquée:

( ( seq 1 100 | tee /dev/fd/2 | head 1>&3 ) 2>&1 | tail ) 3>&1
( ( seq 1 100 | tee /dev/stderr | head 1>&3 ) 2>&1 | tail ) 3>&1

(Aucun de ceux-ci ne fonctionnera en csh ou tcsh.) 

Pour quelque chose avec un peu plus de contrôle, vous pouvez utiliser cette commande Perl:

COMMAND | Perl -e 'my $size = 10; my @buf = (); while (<>) { print if $. <= $size; Push(@buf, $_); if ( @buf > $size ) { shift(@buf); } } print "------\n"; print @buf;'
10
RantingNerd

head -10 file.txt; tail -10 file.txt

En dehors de cela, vous devrez écrire votre propre programme/script.

3
mah
(sed -u 10q; echo ...; tail) < file.txt

Juste une autre variante du thème (head;tail), en évitant le problème initial de remplissage de la mémoire tampon pour les petits fichiers.

2
guest

Il a fallu beaucoup de temps pour arriver à cette solution qui semble être la seule à couvrir tous les cas d'utilisation (jusqu'à présent):

command | tee full.log | stdbuf -i0 -o0 -e0 awk -v offset=${MAX_LINES:-200} \
          '{
               if (NR <= offset) print;
               else {
                   a[NR] = $0;
                   delete a[NR-offset];
                   printf "." > "/dev/stderr"
                   }
           }
           END {
             print "" > "/dev/stderr";
             for(i=NR-offset+1 > offset ? NR-offset+1: offset+1 ;i<=NR;i++)
             { print a[i]}
           }'

Liste des fonctionnalités:

  • sortie en direct pour la tête (évidemment que pour la queue n'est pas possible)
  • pas d'utilisation de fichiers externes
  • progressbar un point pour chaque ligne après MAX_LINES, très utile pour les tâches de longue durée.
  • progressbar sur stderr, assurant que les points de progression sont séparés de la tête et de la queue (très pratique si vous souhaitez utiliser un tuyau standard)
  • évite un possible ordre de journalisation incorrect en raison de la mise en mémoire tampon (stdbuf)
  • évitez de dupliquer la sortie lorsque le nombre total de lignes est inférieur à tête + queue.
2
sorin

le problème ici est que les programmes orientés flux ne connaissent pas la longueur du fichier à l’avance (car il n’en existe peut-être pas, s’il s’agit d’un flux réel).

des outils tels que tail tamponnent les n dernières lignes vues et attendent la fin du flux, puis impriment.

si vous voulez faire cela en une seule commande (et que cela fonctionne avec n'importe quel décalage, et ne répétez pas les lignes si elles se chevauchent), vous devrez émuler ce comportement que j'ai mentionné.

essayez ceci:

awk -v offset=10 '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' yourfile
2
Samus_

10 premières lignes de fichier.ext, puis ses 10 dernières lignes:

cat file.ext | head -10 && cat file.ext | tail -10

Les 10 dernières lignes du fichier, puis les 10 premières:

cat file.ext | tail -10 && cat file.ext | head -10

Vous pouvez ensuite diriger la sortie ailleurs également:

(cat file.ext | head -10 && cat file.ext | tail -10 ) | your_program

1
Paul

J'ai écrit une application python simple pour ce faire: https://Gist.github.com/garyvdm/9970522

Il gère les tuyaux (flux) ainsi que les fichiers.

1

Eh bien, vous pouvez toujours les chaîner ensemble. Comme si, head fiename_foo && tail filename_foo. Si cela ne suffit pas, vous pouvez écrire vous-même une fonction bash dans votre fichier .profile ou tout fichier de connexion que vous utilisez:

head_and_tail() {
    head $1 && tail $1
}

Et, plus tard, appelez-le à partir de votre invite de shell: head_and_tail filename_foo.

1
S.R.I

Basé sur le commentaire de J.F. Sebastian :

cat file | { tee >(head >&3; cat >/dev/null) | tail; } 3>&1

De cette façon, vous pouvez traiter la première ligne et le reste différemment dans un même canal, ce qui est utile pour utiliser des données CSV:

{ echo N; seq 3;} | { tee >(head -n1 | sed 's/$/*2/' >&3; cat >/dev/null) | tail -n+2 | awk '{print $1*2}'; } 3>&1
N * 2 
 2 
 4 
 6 
1
modular

Pour gérer les canaux (flux) ainsi que les fichiers, ajoutez ceci à votre fichier .bashrc ou .profile:

headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { for (i=NR-offset+1; i<=NR; i++) print a[i] }' ; }

Ensuite, vous pouvez non seulement

headtail 10 < file.txt

mais aussi

a.out | headtail 10

(Cela ajoute quand même des lignes vides parasites lorsque 10 dépasse la longueur de l'entrée, contrairement à la vieille a.out | (head; tail). Merci.

Remarque: headtail 10, pas headtail -10.

0
Camille Goudeseune

Je dirais que, selon la taille du fichier, une lecture active de son contenu peut ne pas être souhaitable. Dans ce cas, je pense que quelques scripts Shell simples devraient suffire.

Voici comment j'ai récemment géré cela pour un certain nombre de très gros fichiers CSV que j'analysais:

$ for file in *.csv; do echo "### ${file}" && head ${file} && echo ... && tail ${file} && echo; done

Ceci affiche les 10 premières lignes et les 10 dernières lignes de chaque fichier, tout en affichant le nom du fichier et des ellipsis avant et après.

Pour un seul fichier volumineux, vous pouvez simplement exécuter ce qui suit pour le même effet:

$ head somefile.csv && echo ... && tail somefile.csv
0
Jitsusama
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"

NOTE: la variable aFile contient le chemin complet du fichier.

0
mark_infinite

S'appuyant sur ce que @Samus_ a expliqué ici sur le fonctionnement de la commande de @Aleksandra Zalcman, cette variante est pratique lorsque vous ne pouvez pas localiser rapidement le début de la queue sans compter les lignes.

{ head; echo "####################\n...\n####################"; tail; } < file.txt

Ou si vous commencez à travailler avec autre chose que 20 lignes, un nombre de lignes peut même vous aider.

{ head -n 18; tail -n 14; } < file.txt | cat -n
0
Script Wolf

Pour imprimer les 10 premières et les 10 dernières lignes d'un fichier, vous pouvez essayer ceci:

cat <(head -n10 file.txt) <(tail -n10 file.txt) | less

0
mariana.ft

s'inspirant des idées ci-dessus (testé bash & zsh) 

mais en utilisant un alias 'hat' Head and Tails

alias hat='(head -5 && echo "^^^------vvv" && tail -5) < '


hat large.sql
0
zzapper

Je cherche cette solution depuis un moment. J'ai essayé moi-même avec sed, mais le problème de ne pas connaître la longueur du fichier/flux au préalable était insurmontable. De toutes les options disponibles ci-dessus, j'aime bien la solution awk de Camille Goudeseune. Il a bien noté que sa solution laissait des lignes vierges supplémentaires dans la sortie avec un ensemble de données suffisamment petit. Ici, je propose une modification de sa solution qui supprime les lignes supplémentaires.

headtail() { awk -v offset="$1" '{ if (NR <= offset) print; else { a[NR] = $0; delete a[NR-offset] } } END { a_count=0; for (i in a) {a_count++}; for (i=NR-a_count+1; i<=NR; i++) print a[i] }' ; }
0
Michael Blahay

Pourquoi ne pas utiliser sed pour cette tâche?

sed -n -e 1,+9p -e 190,+9p textfile.txt

0
lik