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.
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.
ed
est le standard text editor
$ echo -e '1+10,$-10d\n%p' | ed -s file.txt
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;'
head -10 file.txt; tail -10 file.txt
En dehors de cela, vous devrez écrire votre propre programme/script.
(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.
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:
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
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
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.
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
.
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
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
.
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
sed -n "1,10p; $(( $(wc -l ${aFile} | grep -oE "^[[:digit:]]+")-9 )),\$p" "${aFile}"
NOTE: la variable aFile contient le chemin complet du fichier.
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
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
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
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] }' ; }
Pourquoi ne pas utiliser sed
pour cette tâche?
sed -n -e 1,+9p -e 190,+9p textfile.txt