Obter o número da linha do desvio de bytes

12

Com deslocamento de bytes para um arquivo.

Existe uma ferramenta que fornece o número da linha para este byte?

  • Contagem de bytes começando com zero, como em: primeiro byte é 0 e não 1.
  • Número da linha começando com 1.
  • O arquivo pode ter texto simples, blobs "binários", caracteres multibyte etc. Mas a seção em que estou interessado: Fim do arquivo, possui apenas ASCII.

Exemplo, arquivo:

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

Tendo byte offset 8que me daria linha 3.

Acho que eu poderia usar algo assim para encontrar o número da linha:

 uma. tail -c+(offset + 1) file | wc -l, aqui +1como tailconta a partir de 1.
 b. wc -l file
 c. Então tail -n+num onde numestáa - b + 1

Mas ... existe uma ferramenta bastante comum que possa me fornecer numdiretamente?


Editar, errar: ou o mais óbvio:

head -c+offset file | wc -l
user367890
fonte
2
Arquivos binários não têm linhas.
Kusalananda
@ Kusalananda: Linhas neste contexto são dados separados por 0x0abytes.
precisa saber é o seguinte
3
Provavelmente não é o que você está perguntando, mas o Vim tem uma função para isso. Ele conta offsets a partir de 1, então: :echo byte2line(offset+1).
Satō Katsura
@SatoKatsura: Sim, e obrigado. Tentei com o vim primeiro. Mas mesmo com vim -be vim+ set binary+ arquivo aberto ele foi corrompido. (Ah. De repente, lembro-me de qual plugin estraga tudo). Mas, de qualquer maneira, como eu uso isso em lotes e em combinação com uma variedade de scripts, o Vim foi abandonado cedo. Mas +1 de qualquer maneira.
precisa saber é o seguinte
@ user367890 Um arquivo binário pode ter 0xaqualquer lugar. O conceito de linhas em um arquivo binário não tem sentido.
precisa saber é o seguinte

Respostas:

14

No seu exemplo,

001
002
003
004

o byte número 8 é a segunda nova linha, não 0a próxima linha.

A seguir, será fornecido o número de linhas completas após $bbytes:

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

Ele reportará 2com bdefinido como 8 e reportará 1com bdefinido como 7.

O ddutilitário, da maneira como é usado aqui, lerá o arquivo data.ine lerá $bblocos de tamanho 1 byte.

Como "icarus" corretamente aponta nos comentários abaixo, o uso bs=1é ineficiente. É mais eficiente, nesse caso específico, trocar bse count:

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

Isso terá o mesmo efeito que o primeiro ddcomando, mas lerá apenas um bloco de $bbytes.

O wcutilitário conta novas linhas, e uma "linha" no Unix sempre é encerrada por uma nova linha. Portanto, o comando acima ainda dirá 2se você definir balgo menor que 12 (a nova linha a seguir). O resultado que você está procurando é, portanto, qualquer que seja o número dos relatórios de pipeline acima, mais 1.

Obviamente, isso também contará as novas linhas aleatórias na parte de blob binário do seu arquivo que precede o texto ASCII. Se você soubesse onde o bit ASCII é iniciado, você pode adicionar skip="$offset"ao ddcomando onde $offsetestá o número de bytes a serem pulados no arquivo.

Kusalananda
fonte
@don_crisstihead: unknown option -- c
Kusalananda
@Kusalananda Você está usando a cabeça do BSD, as opções são diferentes #
Sergiy Kolodyazhnyy 31/01
@Serg :-) Estou bem ciente disso. Não sabemos o que esse OP usa, por isso estou seguindo o POSIX.
Kusalananda
1
Como mencionei no Q: inicia contagem byte com 0, não 1, portanto, 8 == 0 ...
user367890
@ user367890 Nesse caso, use $(( b - 1 )).
Kusalananda
4

Atualmente, não existe uma ferramenta dedicada como essa, embora possa ser feita com bastante facilidade em 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)

O uso é simples:

line4byte.py <FILE> <BYTE>

Execução de teste:

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

Este é um script muito rápido e simples. Ele não verifica se o arquivo está vazio ou não, portanto, funciona apenas em arquivos não vazios.

Sergiy Kolodyazhnyy
fonte
4

Rastreie os bytes vistos e emita o número da linha atual, se o deslocamento especificado estiver dentro da soma:

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

Ou longamente:

#!/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;
agitar
fonte