C'est donc pour les devoirs, mais je ne poserai pas la question spécifique des devoirs.
Je dois utiliser la tête et la queue pour saisir différents ensembles de lignes à partir d'un fichier. Donc, comme les lignes 6-11 et 19-24 et enregistrez-les dans un autre fichier. Je sais que je peux le faire en utilisant append tel que
head -11 file|tail -6 > file1; head -24 file| tail -6 >> file1.
Mais je ne pense pas que nous soyons censés le faire.
Existe-t-il un moyen spécifique de combiner les commandes head et tail, puis de les enregistrer dans le fichier?
head
ettail
? Si c'est le cas, votre solution est à peu près la meilleure que vous puissiez faire. Si vous êtes autorisé à utiliser d'autres programmes,sed
ouawk
si vous autorisez de meilleures solutions (c'est-à-dire avec moins d'appels de processus).>>
) en joignant les deux commandes entre parenthèses pour rediriger leur sortie concaténés:(head -11 file | tail -6; head -24 file | tail -6) > file1
. Cela se résume vraiment à la préférence personnelle qui est plus agréable.Réponses:
Vous pouvez le faire avec
head
l'arithmétique seule et de base, si vous regroupez des commandes en{ ... ; }
utilisant une construction commeoù toutes les commandes partagent la même entrée (merci @mikeserv ).
Obtenir les lignes 6-11 et 19-24 équivaut à:
Donc, en gros, vous exécuteriez:
la source
Vous pouvez utiliser la
{ … }
construction de regroupement pour appliquer l'opérateur de redirection à une commande composée.Au lieu de dupliquer les premières lignes M + N et de ne conserver que le dernier N, vous pouvez ignorer les premières lignes M et dupliquer le N suivant. C'est beaucoup plus rapide sur les gros fichiers . Attention, l'
+N
argument detail
n'est pas le nombre de lignes à sauter, mais un plus cela - c'est le numéro de la première ligne à imprimer avec des lignes numérotées à partir de 1.Dans les deux cas, le fichier de sortie n'est ouvert qu'une seule fois, mais le fichier d'entrée est parcouru une fois pour chaque extrait à extraire. Que diriez-vous de regrouper les entrées?
En général, cela ne fonctionne pas. (Cela peut fonctionner sur certains systèmes, au moins lorsque l'entrée est un fichier normal.) Pourquoi? En raison de la mise en mémoire tampon d'entrée . La plupart des programmes, notamment
tail
, ne lisent pas leur octet d'entrée octet, mais quelques kilo-octets à la fois, car c'est plus rapide.tail
Lit donc quelques kilo-octets, saute un peu au début, passe un peu plus àhead
et s'arrête - mais ce qui est lu est lu et n'est pas disponible pour la commande suivante.Une autre approche consiste à utiliser des
head
tuyaux/dev/null
pour sauter des lignes.Encore une fois, ce n'est pas garanti de fonctionner, en raison de la mise en mémoire tampon. Il se trouve que cela fonctionne avec la
head
commande de GNU coreutils (celle que l'on trouve sur les systèmes Linux non intégrés), lorsque l'entrée provient d'un fichier normal. En effet, une fois que cette implémentation dehead
a lu ce qu'elle veut, elle définit la position du fichier sur le premier octet qu'elle n'a pas sorti. Cela ne fonctionne pas si l'entrée est un tuyau.Un moyen plus simple d'imprimer plusieurs séquences de lignes à partir d'un fichier est d'appeler un outil plus généraliste tel que sed ou awk . (Cela peut être plus lent, mais cela n'a d'importance que pour les fichiers extrêmement volumineux.)
la source
Je sais que vous avez dit que vous devez utiliser la tête et la queue, mais sed est certainement l'outil le plus simple pour le travail ici.
Vous pouvez même créer les blocs dans une chaîne avec un autre processus et l'exécuter via sed.
-n annule la sortie, puis vous spécifiez des plages à imprimer avec p, avec le premier et le dernier numéro de la plage séparés par une virgule.
Cela étant dit, vous pouvez soit effectuer le regroupement de commandes suggéré par @don_crissti, soit parcourir le fichier plusieurs fois avec head / tail saisissant un morceau de lignes à chaque fois que vous passez.
Plus il y a de lignes dans un fichier et plus vous avez de blocs, plus sed sera efficace.
la source
Avec
sed
vous pourriez faire:... Peut-être une solution plus efficace pourrait être trouvée avec
head
. Don a déjà montré comment cela pouvait très bien fonctionner, mais j'ai aussi joué avec. Quelque chose que vous pourriez faire pour gérer ce cas spécifique:... qui appellerait
head
4 fois l'écriture versoutfile
ou vers/dev/null
selon que la valeur de cette itération$n
est un nombre pair ou impair.Pour des cas plus généraux, j'ai bricolé cela à partir d'autres choses que j'avais déjà:
Cela peut faire votre chose comme:
... qui imprime ...
Il s'attend à ce que son premier argument soit un nombre de répétitions préfixé par un
-
, ou, à défaut, juste un-
. Si un décompte est fourni, il répétera le modèle de ligne donné dans les arguments suivants autant de fois que spécifié et s'arrêtera dès qu'il l'aura fait.Pour chaque argument qui suit, il interprétera un entier négatif pour indiquer un compte de lignes qui doit être écrit
/dev/null
et un entier positif pour indiquer un compte de lignes qui doit être écritstdout
.Ainsi, dans l'exemple ci-dessus, il imprime les 5 premières lignes
/dev/null
, les 6 suivantesstdout
, les 7 suivantes/dev/null
et les 6 suivantes à nouveaustdout
. Après avoir atteint le dernier de ses arguments et entièrement parcouru le-1
nombre de répétitions, il se ferme ensuite. Si le premier argument avait été,-2
il aurait répété le processus une fois de plus, ou si-
aussi longtemps qu'il le pouvait.Pour chaque cycle d'arg, la
while
boucle est traitée une fois. En haut de chaque boucle, la première ligne destdin
est lue dans la variable shell$l
. Ceci est nécessaire carwhile head </dev/null; do :; done
se répétera indéfiniment -head
indique dans son retour quand il a atteint la fin du fichier. Ainsi, la vérification par rapport à EOF est dédiéeread
etprintf
n'écrira$l
plus une nouvelle lignestdout
que si le deuxième argument est un entier positif.La
read
vérification complique un peu la boucle car immédiatement après l'appel d'une autre boucle - unefor
boucle qui itère sur les arguments2-$#
comme représenté dans$n
chaque itération de sawhile
boucle parent . Cela signifie que pour chaque itération, le premier argument doit être décrémenté de un de la valeur spécifiée sur la ligne de commande, mais tous les autres doivent conserver leurs valeurs d'origine, et donc la valeur du$_n
marqueur var est soustraite de chacun, mais ne contient que valeur supérieure à 0 pour le premier argument.Cela constitue la boucle principale de la fonction, mais la majeure partie du code est en haut et est destinée à permettre à la fonction de tamponner proprement même un tuyau en entrée. Cela fonctionne en appelant d'abord un arrière-plan
dd
pour copier son entrée dans un fichier tmp en sortie à des blocs de 4k par morceau. La fonction établit ensuite une boucle d'attente - qui ne devrait presque jamais terminer même un seul cycle complet - juste pour s'assurer qu'elledd
a effectué au moins une seule écriture dans le fichier avant que la fonction ne remplace ensuite son stdin par un descripteur de fichier lié au fichier tmp et dissocie ensuite immédiatement le fichier avecrm
. Cela permet à la fonction de traiter le flux de manière fiable sans nécessiter d'interruptions ou autrement pour le nettoyage - dès que la fonction libère sa revendication sur le fd, le fichier tmp cessera d'exister car son seul lien de système de fichiers nommé a déjà été supprimé.la source
Utilisez une fonction bash comme celle-ci:
C'est un peu exagéré dans ce cas, mais si vos filtres grossissent, cela peut devenir une aubaine.
la source