Modificação com script "Multipass" de arquivo grande no local (nível do sistema de arquivos)?

Modificação com script "Multipass" de arquivo grande no local (nível do sistema de arquivos)?

Acabei de chegar ao problema de ter que cortar algumas linhas de um arquivo grande (gigabyte) e, estando ciente do potencial consumo de CPU ao tentar lê-lo na memória, queria editá-lo no local ... e então me deparei com estas questões:

... e ainda estes:

No entanto, eu estava pensando em outra coisa: acredito (mas não tenho certeza) que qualquer sistema de arquivos (como ext3) teria que empregar algo como uma lista vinculada, para poder descrever algo como fragmentos de um arquivo que são mapeado para áreas do disco.

Assim, deveria ser possível fazer algo assim - por exemplo, digamos, eu tenho um arquivo bigfile.datcomo este (os números devem indicar o deslocamento de bytes, mas é um pouco difícil alinhá-los):

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

L 1\n L 2\n L 3\n L 4\n L 5\n L 6\n

Este arquivo poderia então, em princípio, ser carregado em um aplicativo de terminal para navegação - vamos imaginar que chamamos um tool editsegments bigfile.dat, e digamos que seja semelhante a como less -N bigfile.datexibiria o mesmo arquivo (com números de linha):

      1      1      L 1
      2      2      L 2 *
      3      3      L 3
      4      4      L 4 *
      5      5      L 5
      6      6      L 6
bigfile.dat (END) 

Digamos que eu poderia inserir um comando lá (digamos, dpara excluir linhas), clicar em outra tecla ou no mouse onde está indicado acima *- o que significa que tudo entre as linhas 2 e 4 deve ser excluído. O programa então responderia com isto sendo mostrado:

      1      1      L 1
      2      5      L 5
      3      6      L 6
bigfile.dat (END) 

Agora podemos ver que a primeira coluna mais à esquerda mostra o número da linha "nova" (após o corte), a segunda coluna é o número da linha "antiga" (antes do corte) - e então o conteúdo real da linha segue.

Agora, o que imagino que aconteça depois que esse pseudoaplicativo editsegmentsfor encerrado, é que, antes de mais nada, bigfile.datele permanece intocado; entretanto, agora haveria também um arquivo de texto extra no mesmo diretório, digamos bigfile.dat.segments; com estes conteúdos:

d 4:15 # line 2-4

... e além disso, um arquivo especial (como um "link simbólico") - vamos chamá-lo bigfile.dat.iedit- apareceria.

Agora, basicamente, o resultado de tudo isso seria que, se eu tentar abrir bigfile.dat.ieditcom algo como less -N bigfile.dat.iedit, gostaria de obter o conteúdo "editado":

      1 L 1
      2 L 5
      3 L 6
bigfile.dat (END) 

... o que poderia ser alcançado, eu acho, instruindo de alguma forma o sistema operacional, que quando $FILE.ieditfor aberto, primeiro $FILE.segmentsdeve ser aberto e lido; o d 4:15instruiria que os bytes 4 a 15 no arquivo original deveriam ser omitidos - o que resultaria em algo como:

0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23

L 1\n L2\n L3\n L4\n L 5\n L 6\n

0 1 2 3 ------------------------------->16 17 18 19 20 21 22 23

Em outras palavras -assumindoque em um conceito de sistema de arquivos de arquivo, cada byte de conteúdo também contém um "link" para o próximo byte na cadeia - deve ser possível instruir o sistema de arquivos a estabelecer uma nova lista vinculada com base em um script e fornecer o conteúdo conforme representado por esta lista vinculada modificada por meio de um arquivo especial (link simbólico ou pipe).

Isso é o que eu quis dizer com "script" no título - que a "nova" lista vinculada pode ser controlada por um arquivo de script ( $FILE.segments), editável pelo usuário em um editor de texto (ou gerado por um aplicativo front-end). O que quero dizer com “multipass” é o fato de que bigfile.datneste processo não há nenhuma modificação; então eu poderia editar o primeiro gigabyte (original) hoje, salvando o progresso em ( $FILE.segments) - então eu poderia editar o segundo gigabyte amanhã, salvando novamente o progresso em ( $FILE.segments) etc. - o tempo todo, o original bigfile.datpermanece inalterado.

Quando todas as edições forem concluídas, provavelmente seria possível chamar uma espécie de comando (digamos, editsegments --finalize bigfile.dat), que simplesmente codificaria permanentemente a nova lista vinculada como o conteúdo de bigfile.dat(e, de acordo com isso, remover bigfile.dat.segmentse bigfile.dat.iedit). Ou ainda mais fácil, pode-se simplesmente fazer:

cp bigfile.dat.iedit /path/to/somewhere/else/bigfile.modified.dat

É claro que, além de um dcomando de script elete, rtambém se poderia ter um comando eplace, digamos:

r 16:18 AAA 

... dizendo: substitua o conteúdo entre os bytes 16 e 18 pelos próximos 18-16+1=3 bytes após o espaço (ou seja, o AAA) - a lista vinculada poderia de fato "enganchar" no próprio conteúdo do comando de script ( o gráfico abaixo contendo também o delete):

0 1 2 3 4 5 6 7 8 9 10 11 12,3,4 15 16 17 18 19 20 21 22 23

L 1\n L2\n L3\n L4\n L 5\n L 6\n

0 1 2 3 ------------------------------->| | 19 20 21 22 23

. . ...\n r1  6  :18  AAA \n  . .  . .


Agora, acho que programas como hexedit(como mencionadoaqui) altere os arquivos no local - mas eu gostaria apenas do benefício da possibilidade de script (ainda melhor se pudesse ser regulado por um aplicativo GUI, mesmo que seja um terminal) e o benefício de não ter realmente o arquivo original alterado, até que se confirme que todas as edições são conforme necessário.

Não tenho certeza se algo assim é possível - e mesmo que seja, acho que pode exigir um driver dedicado (em vez de apenas um programa de usuário)... Mas acho que vale a pena perguntar de qualquer maneira - existe algo assim para Linux?

Muito obrigado antecipadamente por qualquer resposta,
Felicidades!

Responder1

A estrutura dos arquivos no disco depende do sistema de arquivos em uso. Nenhum dos sistemas de arquivos do mundo real usa listas vinculadas conforme você descreve (isso tornaria fseek(3)insuportável). A coisa mais próxima disso é a MicrosoftGORDO, essencialmente movendo os ponteiros dos blocos de dados para uma matriz que os sombreia.

Mas a maioria dos sistemas de arquivos usa algumas referências baseadas em ponteiros para blocos de dados no arquivo, então, em princípio, alguém poderia cortar um bloco de um arquivo apenas embaralhando um punhado de ponteiros (não todo o conteúdo do arquivo) e marcando um bloco no meio do arquivo como gratuito. Infelizmente, essa não é uma operação muito útil, os blocos de arquivo são bastante grandes (normalmente 4KiB) e raramente se alinham razoavelmente com as estruturas do arquivo (sejam linhas ou outras subdivisões).

Responder2

O que você descreve parece muito com umrepetirde um editor de textolista de refazercontra o arquivo original inalterado ao quallista de refazerpertence. Tenho certeza que gvimtem um talpersistentelista de desfazer/refazer, que você pode (?) ser capaz de utilizar, e eu sei que emacsdefinitivamente tem uma lista que você provavelmente poderia persuadir a fazer o que quiser (por meio de um elispscript), por exemplo.Salvar o histórico de desfazer do Emacs entre sessões.

Como observação lateral, desligar todas as ações indesejadas pode ser uma boa ideia para arquivos tão grandes, por exemplo:salvamento automático,destaque de sintaxe(lento em umgrandearquivo emacs), etc. e o emacs em um sistema de 32 bits tem 256 MBlimite de tamanho de arquivo.

Certamente não será tão conciso quanto o que você sugeriu, mas pode ser útil se não houver um grande número de alterações.

Responder3

Geralmente, você não pode editar um arquivo sem colocar o arquivo inteiro na memória. Presumo que o que você realmente deseja fazer é apenas ter um novo arquivo que seja uma cópia do antigo, sem linhas específicas. Isso pode ser feito facilmente usando os utilitários unix heade tail. Por exemplo, para copiar tudo, exceto as linhas 5, 12 e 52 de um arquivo, você pode fazer

head -n 4 bigfile.dat > tempfile.dat
tail -n +6 bigfile.dat | head -n 6 >> tempfile.dat 
tail -n +13 bigfile.dat | head -n 39 >> tempfile.dat 
tail -n 53 bigfile.dat >> tempfile.dat

Caso você não esteja familiarizado com esses utilitários, explicarei com mais detalhes.

O headutilitário imprime as primeiras n linhas de um arquivo. Se não receber um argumento posicional, ele usará a entrada padrão como arquivo. A -nbandeira informa quantas linhas imprimir. Portanto, head -n 2imprimirá apenas as 2 primeiras linhas da entrada padrão.

O tailutilitário imprime as últimas n linhas de um arquivo. Assim como o head, ele pode ler um arquivo ou entrada padrão. O sinalizador -n informa ao tail quantas linhas imprimir a partir do final. Você também pode prefixar o número com um sinal de mais para dizer ao tail para imprimir as linhas do final do arquivo, começando com tantas linhas desde o início. Por exemplo, tail -n 2imprime as duas últimas linhas da entrada padrão. No entanto, tail -n +2imprime todas as linhas começando com a linha número 2 (omite a linha 1).

Então, em geral, se você quiser imprimir linhas no intervalo [x, y) de um arquivo, você faria

`tail -n +x | head -n d`

onde d = y - x. Esses comandos produzirão um novo arquivo. Você pode então excluir o arquivo antigo, se desejar. A vantagem de fazer dessa forma é que headvocê tailsó precisa manter uma linha na memória por vez, para não encher rapidamente sua RAM.

Responder4

Parece um trabalho para um script sed. IIRC foi projetado para tais tarefas. Processamento linha por linha, processamento repetido do mesmo grupo de comandos e regex, todos combinados em uma ferramenta. Embora eu saiba que isso funcionará, não posso orientá-lo além de direcioná-lo para a multapágina de manual.

informação relacionada