Est-il possible en bash, de démarrer la lecture d'un fichier à partir d'un décalage de comptage d'octets arbitraire?


22

Je veux localiser une date quelque part dans un journal de 8 Go (texte).

Puis-je contourner une lecture séquentielle complète et faire d'abord des séparations binaires du fichier (taille), ou en quelque sorte naviguer dans le système de fichiers inodes(que je connais très peu), pour commencer la lecture à partir de chaque point de partage, jusqu'à ce que je trouve un décalage approprié à partir de où commencer ma recherche de texte pour une ligne contenant la date?

tailLa lecture de la dernière ligne n'utilise pas une lecture séquentielle normale, donc je me demande si cette fonctionnalité est en quelque sorte disponible en bash, ou aurais-je besoin d'utiliser Python ou C / C ++ ... mais je suis spécifiquement intéressé par une bashoption ..


Réponses:


8
for (( block = 0; block < 16; block += 1 ))
do 
    echo $block; 
    dd if=INPUTFILE skip=$((block*512))MB bs=64 count=1 status=noxfer 2> /dev/null | \
        head -n 1
done

qui .. ne crée aucun fichier à division temporaire, ignore les blocs * 512 Mo de données à chaque exécution, lit 64 octets à partir de cette position et limite la sortie à la première ligne de ces 64 octets.

vous voudrez peut-être ajuster 64 à tout ce dont vous pensez avoir besoin.


@akira .. Cela a l'air vraiment bien, mais je veux y regarder un peu plus d'abord .. (donc, jusqu'à demain .....
Peter.O

1
@akira .. 'dd' est génial. Cela fonctionne bien avec la recherche de division binaire ... Je peux maintenant extraire une ligne regex'd (par sa clé Date), à ​​partir d'un fichier 8G trié en moins de 1 seconde ... Il semble donc que j'atteindrai mon 3 deuxième cible personnelle pour extraire une plage de dates entre deux clés (inclus) .. excluant le temps de sortie, qui varie en fonction de la quantité qui est sortie .. J'utiliserai ddpour ça aussi ... C'est un excellent outil! :)
Peter.O

30

Cela ressemble à ce que vous voulez:

tail -c +1048576

ou quel que soit le nombre d'octets que vous souhaitez ignorer. Le signe plus indique à tail de mesurer depuis le début du fichier au lieu de la fin. Si vous utilisez la version GNU de tail, vous pouvez l'écrire comme suit:

tail -c +1M

Pour obtenir un nombre fixe d'octets après la coupe, au lieu de tout le reste du fichier, il suffit de le diriger à travers head:

tail -c +1048576 | head -c 1024

La flexibilité Linux / bash est géniale (j'ai définitivement passé trop de temps à passer à Linux). Je venais d'accepter la réponse d'Akira, mais je l'ai tirée jusqu'à ce que j'évalue cela plus complètement. ddsaute à un octet spécifique (comme le fait tail), mais c'est une douleur codant autour de longueurs de ligne inconnues, puis un appel à sed pour supprimer les lignes partielles principales ... Il semble que tail | head puisse faire cela sans douleur (aussi vite?) . Je ne comprends pas comment la tête peut fermer le robinet sur la queue, mais il semble que ce soit :) Il doit s'agir de: Si la tête cesse de recevoir, la queue cesse d'envoyer (et arrête la lecture). Doit y retourner .. demain.
Peter.O

@ fred.bear: tail/ headne sont pas en mesure de deviner les longueurs de ligne également. vous devez sauter à la position x, puis vous pouvez regarder à gauche ou à droite de x pour le suivant \n. peu importe le nom du programme. ainsi, dans les deux cas, vous passez à x, puis utilisez headpour regarder vers la droite la fin de ligne suivante.
akira

tail|headoffre la possibilité de ne pas se soucier du tout de ddcount = val. Avec 'dd', si je ne récupère pas assez de données, c'est "game over". La flexibilité des longueurs de ligne arbitraires est grande. J'ai écrit une fonction pour 'dd' qui retourne la ligne complète "la plus proche" et son décalage, mais je préfère éviter le problème de longueur. J'ai maintenant testé tail | head, et il fonctionne initialement bien (à offset = 100 Mo), mais ralentit considérablement pour prendre 2 min pour un accès à offset = 8 Go (je peux le awkfaire en 1 min) ... donc c'est génial pour les fichiers plus petits .. Merci de m'avoir informé du combo queue / tête :)
Peter.O

2

J'essaierais quelque chose comme ça pour diviser le journal en morceaux de 512 Mo pour une analyse plus rapide.

split <filename> -b 536870912

Si vous recherchez le fichier, les éléments suivants fonctionneraient:

for file in x* ; do
  echo $file
  head -n 1 $file
done

Utilisez cette sortie pour déterminer quel fichier grep pour votre date.


Merci, mais c'est plus lent qu'une recherche séquentielle. Jetez un œil à mes commentaires ici unix.stackexchange.com/questions/8121/… (plutôt que de réécrire la même chose ici)
Peter.O

en utilisant «split», vous touchez chaque octet une fois. Si vous faites cela, vous pouvez également récupérer l'ensemble des 8 Go.
akira

@sifusam .. Je veux faire une recherche de division binaire (pas seulement diviser les fichiers) en.wikipedia.org/wiki/Binary_search_algorithm ... donc c'était une bonne réponse pour une question différente :) .. Merci d'avoir répondu .. +1 pour vous lancer ....
Peter.O

0

Voici mon script, je recherche la première ligne où le premier champ correspond à mon numéro. Les lignes sont triées selon le premier champ. J'utilise dd pour vérifier la première ligne de blocs de 128 Ko, puis je passe au bloc et j'effectue une recherche. Il améliore l'efficacité si le fichier dépasse 1M.

Tout commentaire ou correction est apprécié!

#!/bin/bash

search=$1;
f=$2;

bs=128;

max=$( echo $(du $f | cut -f1)" / $bs" | bc );
block=$max;
for i in $(seq 0 $max); do
 n=$(dd bs=${bs}K skip=$i if=$f 2> /dev/null| head -2 | tail -1 | cut -f1)
 if [ $n -gt $search ]; then
  block=`expr $i - 1` 
  break;
 fi
done; 
dd bs=${bs}K skip=$block if=$f 2> /dev/null| tail -n +2 | awk -v search="$search" '$1==search{print;exit 1;};$1>search{exit 1;};';

* EDIT * ** grep est beaucoup plus rapide et ack encore meilleur

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.