Obtenir le numéro de ligne à partir du décalage d'octet

12

Décalage d'octets pour un fichier.

Existe-t-il un outil qui donne le numéro de ligne pour cet octet?

  • Nombre d'octets commençant par zéro, comme dans: le premier octet est 0 et non 1.
  • Numéro de ligne commençant par 1.
  • Le fichier peut avoir à la fois du texte brut, des blobs "binaires", des caractères multi-octets, etc. Mais la section qui m'intéresse: Fin du fichier, n'a que ASCII.

Exemple, fichier:

001
002
003  <<-- first zero on this line is byte 8
004

Avoir un décalage d'octet 8qui me donnerait une ligne 3.

Je suppose que je pourrais utiliser quelque chose comme ça pour trouver le numéro de ligne:

 une. tail -c+(offset + 1) file | wc -l, ici +1pour tail1.
 b. wc -l file
 c. Alors tail -n+numnumesta - b + 1

Mais ... existe-t-il un outil assez courant qui peut me donner numdirectement?


Modifier, errer: ou le plus évident:

head -c+offset file | wc -l
user367890
la source
2
Les fichiers binaires n'ont pas de lignes.
Kusalananda
@Kusalananda: Dans ce contexte, les lignes sont des données séparées par des 0x0aoctets.
user367890
3
Probablement pas ce que vous demandez, mais Vim a une fonction pour cela. Il compte des décalages de 1, donc: :echo byte2line(offset+1).
Satō Katsura
@SatoKatsura: Oui, et merci. J'ai d'abord essayé avec vim. Mais même avec vim -bet vim+ set binary+ fichier ouvert, il a été corrompu. (Ah. Soudain, je me souviens quel plugin le gâche). Mais, de toute façon, comme je l'utilise par lots et en combinaison avec une gamme de scripts, Vim a été abandonné très tôt. Mais +1 quand même.
user367890
@ user367890 Un fichier binaire peut avoir 0xan'importe où. Le concept de lignes dans un fichier binaire n'a pas de sens.
user207421

Réponses:

14

Dans votre exemple,

001
002
003
004

l'octet numéro 8 est le deuxième saut de ligne, pas celui 0de la ligne suivante.

Ce qui suit vous donnera le nombre de lignes complètes après $boctets:

$ dd if=data.in bs=1 count="$b" | wc -l

Il fera rapport 2avec un bensemble à 8 et il fera un rapport 1avec un bensemble à 7.

L' ddutilitaire, tel qu'il est utilisé ici, lira le fichier data.inet lira les $bblocs de taille 1 octet.

Comme le souligne à juste titre "icarus" dans les commentaires ci-dessous, l'utilisation bs=1est inefficace. Il est plus efficace, dans ce cas particulier, d'échanger bset count:

$ dd if=data.in bs="$b" count=1 | wc -l

Cela aura le même effet que la première ddcommande, mais ne lira qu'un seul bloc d' $boctets.

L' wcutilitaire compte les sauts de ligne et une "ligne" sous Unix se termine toujours par un saut de ligne. Ainsi, la commande ci-dessus indiquera toujours 2si vous définissez bune valeur inférieure à 12 (la nouvelle ligne suivante). Le résultat que vous recherchez est donc le nombre de rapports de pipeline ci-dessus, plus 1.

Cela comptera évidemment aussi les sauts de ligne aléatoires dans la partie binaire blob de votre fichier qui précède le texte ASCII. Si vous saviez où le bit ASCII commence, vous pouvez ajouter skip="$offset"à la ddcommande, où $offsetest le nombre d'octets à passer dans le fichier.

Kusalananda
la source
@don_crisstihead: unknown option -- c
Kusalananda
@Kusalananda Vous utilisez la tête BSD, les options sont différentes
Sergiy Kolodyazhnyy
@Serg :-) J'en suis bien conscient. Nous ne savons pas ce que l'OP utilise, donc je m'en tiens à POSIX.
Kusalananda
1
Comme je l'ai mentionné dans Q: le nombre d'octets commence par 0, pas 1, donc 8 == 0 ...
user367890
@ user367890 Dans ce cas, utilisez $(( b - 1 )).
Kusalananda
4

Actuellement, il n'y a pas d'outil dédié comme ça, bien que cela puisse être fait assez facilement en python:

#!/usr/bin/env python3
import sys
import os

offset = int(sys.argv[2])
newline = 1
with open(sys.argv[1]) as fd:
    fd.seek(offset)
    while True:
        try:
            byte = fd.read(1)
            if byte == '\n': newline+=1
            #print(byte)
            offset = offset - 1
            fd.seek(offset)
        except ValueError:
            break
print(newline)

L'utilisation est simple:

line4byte.py <FILE> <BYTE>

Essai:

$ cat input.txt
001
002
003
004
$ chmod +x ./line4byte.py                                                     
$ ./line4byte.py input.txt 8                                                  
3

Ceci est un script très rapide et simple. Il ne vérifie pas si le fichier est vide ou non, il ne fonctionne donc que sur les fichiers non vides.

Sergiy Kolodyazhnyy
la source
4

Suivez les octets vus et émettez le numéro de ligne actuel si le décalage donné est dans la somme:

perl -E '$off=shift;while(<>){$sum+=length;if($sum>=$off){say $.;exit}}' 8 file

Ou longuement:

#!/usr/bin/env perl
use strict;
use warnings;
die "Usage: $0 offset file|-\n" if @ARGV != 2;
my $offset = shift;
shift if $ARGV[0] eq '-';
my $sum;
while (readline) {
    $sum += length;
    if ($sum >= $offset) {
        print "$.\n";
        exit;
    }
}
exit 1;
branler
la source