AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 416767
Accepted
iBug
iBug
Asked: 2018-01-14 00:37:29 +0800 CST2018-01-14 00:37:29 +0800 CST 2018-01-14 00:37:29 +0800 CST

Remover bytes nulos do final de um arquivo grande

  • 772

Acabei de fazer backup do cartão microSD do meu Raspberry Pi no meu PC executando uma distribuição Linux usando este comando:

dd if=/dev/sdx of=file.bin bs=16M

O cartão microSD está apenas 3/4 cheio, então suponho que haja alguns shows de bytes nulos no final do tremendo arquivo. Tenho certeza de que não preciso disso. Como posso retirar esses bytes nulos do final de forma eficiente para que eu possa restaurá-los posteriormente com este comando?

cat file.bin /dev/zero | dd of=/dev/sdx bs=16M
files dd
  • 6 6 respostas
  • 4976 Views

6 respostas

  • Voted
  1. Best Answer
    John1024
    2018-01-14T00:55:51+08:002018-01-14T00:55:51+08:00

    Para criar uma cópia de backup de um disco enquanto economiza espaço, use gzip:

    gzip </dev/sda >/path/to/sda.gz
    

    Quando você quiser restaurar o disco do backup, use:

    gunzip -c /path/to/sda.gz >/dev/sda
    

    Isso provavelmente economizará muito mais espaço do que simplesmente remover os bytes NUL à direita.

    Removendo bytes NUL à direita

    Se você realmente deseja remover bytes NUL à direita e possui GNU sed, tente:

    sed '$ s/\x00*$//' /dev/sda >/path/to/sda.stripped
    

    Isso pode ser um problema se os dados de um disco grande excederem algum limite interno de sed. Embora o GNU sed não tenha um limite embutido no tamanho dos dados, o manual do GNU sed explica que as limitações de memória do sistema podem impedir o processamento de arquivos grandes:

    GNU sed não tem limite embutido no comprimento da linha; contanto que possa malloc() mais memória (virtual), você pode alimentar ou construir linhas enquanto quiser.

    No entanto, a recursão é usada para lidar com subpadrões e repetição indefinida. Isso significa que o espaço de pilha disponível pode limitar o tamanho do buffer que pode ser processado por determinados padrões.

    • 8
  2. zqb-all
    2020-04-07T17:10:19+08:002020-04-07T17:10:19+08:00

    Você pode escrever uma ferramenta simples para resolver esse problema.

    Leia o arquivo, descubra o último byte válido (não nulo) e trunque o arquivo.

    Um exemplo em ferrugem de https://github.com/zqb-all/cut-trailing-bytes :

    use std::io;
    use std::io::prelude::*;
    use std::fs::File;
    use std::fs::OpenOptions;
    use std::path::PathBuf;
    use structopt::StructOpt;
    use std::num::ParseIntError;
    
    fn parse_hex(s: &str) -> Result<u8, ParseIntError> {
        u8::from_str_radix(s, 16)
    }
    
    #[derive(Debug, StructOpt)]
    #[structopt(name = "cut-trailing-bytes", about = "A tool for cut trailing bytes, default cut trailing NULL bytes(0x00 in hex)")]
    struct Opt {
        /// File to cut
        #[structopt(parse(from_os_str))]
        file: PathBuf,
    
        /// For example, pass 'ff' if want to cut 0xff
        #[structopt(short = "c", long = "cut-byte", default_value="0", parse(try_from_str = parse_hex))]
        byte_in_hex: u8,
    
        /// Check the file but don't real cut it
        #[structopt(short, long = "dry-run")]
        dry_run: bool,
    }
    
    
    fn main() -> io::Result<()> {
    
        let opt = Opt::from_args();
        let filename = &opt.file;
        let mut f = File::open(filename)?;
        let mut valid_len = 0;
        let mut tmp_len = 0;
        let mut buffer = [0; 4096];
    
        loop {
            let mut n = f.read(&mut buffer[..])?;
            if n == 0 { break; }
            for byte in buffer.bytes() {
                match byte.unwrap() {
                    byte if byte == opt.byte_in_hex => { tmp_len += 1; }
                    _ => {
                        valid_len += tmp_len;
                        tmp_len = 0;
                        valid_len += 1;
                    }
                }
                n -= 1;
                if n == 0 { break; }
            }
        }
        if !opt.dry_run {
            let f = OpenOptions::new().write(true).open(filename);
            f.unwrap().set_len(valid_len)?;
        }
        println!("cut {} from {} to {}", filename.display(), valid_len + tmp_len, valid_len);
    
        Ok(())
    }
    
    • 2
  3. letmaik
    2020-04-22T08:40:17+08:002020-04-22T08:40:17+08:00

    Eu tentei o comando de John1024 sede funcionou na maioria das vezes, mas para alguns arquivos grandes ele não cortou corretamente. O seguinte sempre funcionará:

    python -c "open('file-stripped.bin', 'wb').write(open('file.bin', 'rb').read().rstrip(b'\0'))"
    

    Observe que isso carrega o arquivo na memória primeiro. Você pode evitar isso escrevendo um script Python adequado que processe o arquivo em partes.

    • 1
  4. Ella Jameson
    2022-05-05T21:12:41+08:002022-05-05T21:12:41+08:00

    Prefácio

    Acabei de resolver esse problema sozinho com o Python. É simples na teoria, mas na prática requer um pouco de código para funcionar corretamente. Eu queria compartilhar meu trabalho aqui para que outros não tenham que descobrir isso sozinhos.

    O jeito simples (ruim)

    O método mais simples (publicado anteriormente por letmaik) é carregar o arquivo na memória como uma string de bytes, usar o Python .rstrp()para remover bytes nulos à direita da bytestring e, em seguida, salvar essa bytestring sobre o arquivo original.

    def strip_file_blank_space(filename):
        # Strips null bytes at the end of a file, and returns the new file size
        # This will process the file all at once
        # Open the file for reading bytes (then close)
        with open(filename, "rb") as f:
            # Read all of the data into memory
            data = f.read()
        # Strip trailing null bytes from the data in-memory
        data = data.rstrip(b'\x00')
        # Open the file for writing bytes (then close)
        with open(filename, "wb") as f:
            # Write the data from memory to the disk
            f.write(data)
        # Return the new file size
        return(len(data))
    
    new_size = strip_file_blank_space("file.bin")
    

    Isso provavelmente funcionará na maioria das vezes, supondo que o arquivo seja menor que a memória disponível do sistema. Mas com arquivos maiores (32+ GB) ou em sistemas com menos RAM (Raspberry Pi), o processo travará o computador ou será encerrado pelo gerenciador de memória do sistema.

    O Caminho Difícil (Correto)

    A única maneira de contornar o problema de memória limitada é carregar um pequeno bloco de dados por vez, processá-lo, excluí-lo da memória e repetir no próximo pequeno bloco até que todo o arquivo seja processado. Normalmente dá para fazer isso em python com código bem compacto, mas como precisamos processar em blocos a partir do final do arquivo, voltando, dá um pouco mais de trabalho.

    Eu fiz o trabalho para você. Aqui está:

    import os
    import shutil
    from math import floor
    import warnings
    import tempfile
    
    def strip_file_blank_space(filename, block_size=1*(1024*1024)):
        # Strips null bytes at the end of a file, and returns the new file size
        # This will process the file in chunks, to conserve memory (default = 1 MiB)
        file_end_loc = None # This will be used if the file is larger than the block size
        simple_data = None # This is used if the file is smaller than the block size
        # Open the source file for reading
        with open(filename, "rb") as f:
            # Get original file size
            filesize = os.fstat(f.fileno()).st_size
            # Test if file size is less than (or equal to) the block size
            if filesize <= block_size:
                # Load data to do a normal rstrip all in-memory
                simple_data = f.read()
            # If the file is larger than the specified block size
            else:
                # Compute number of whole blocks (remainder at beginning processed seperately)
                num_whole_blocks = floor(filesize / block_size)
                # Compute number of remaining bytes
                num_bytes_partial_block = filesize - (num_whole_blocks * block_size)
                # Go through each block, looking for the location where the zeros end
                for block in range(num_whole_blocks):
                    # Set file position, relative to the end of the file
                    current_position = filesize - ((block+1) * block_size)
                    f.seek(current_position)
                    # Read current block
                    block_data = f.read(block_size)
                    # Strip current block from right side
                    block_data = block_data.rstrip(b"\x00")
                    # Test if the block data was all zeros
                    if len(block_data) == 0:
                        # Move on to next block
                        continue
                    # If it was not all zeros
                    else:
                        # Find the location in the file where the real data ends
                        blocks_not_processed = num_whole_blocks - (block+1)
                        file_end_loc = num_bytes_partial_block + (blocks_not_processed * block_size) + len(block_data)
                        break
                # Test if the end location was not found in the full blocks loop
                if file_end_loc == None:
                    # Read partial block at the beginning of the file
                    f.seek(0)
                    partial_block_data = f.read(num_bytes_partial_block)
                    # Strip from the right side
                    partial_block_data = partial_block_data.rstrip(b"\x00")
                    # Test if this block (and therefore the entire file) is zeros
                    if len(partial_block_data) == 0:
                        # Warn about the empty file
                        warnings.warn("File was all zeros and will be replaced with an empty file")
                    # Set the location where the real data begins
                    file_end_loc = len(partial_block_data)
        
        # If we are doing a normal strip:
        if simple_data != None:
            # Strip right trailing null bytes
            simple_data = simple_data.rstrip(b'\x00')
            # Directly replace file
            with open(filename, "wb") as f:
                f.write(simple_data)
                new_filesize = os.fstat(f.fileno()).st_size
            # Return the new file size
            return len(simple_data)
        # If we are doing a block-by-block copy and replace
        else:
            # Create temporary file (do not delete, will move myself)
            temp_file = tempfile.NamedTemporaryFile(mode="wb", delete=False)
            # Open the source file for reading
            with open(filename, "rb") as f:
                # Test if data is smaller than (or equal to) the block size
                if file_end_loc <= block_size:
                    # Do a direct copy
                    f.seek(0)
                    data = f.read(file_end_loc)
                    temp_file.write(data)
                    temp_file.close()
                # If the data is larger than the block size
                else:
                    # Find number of whole blocks to copy
                    num_whole_blocks_copy = floor(file_end_loc / block_size)
                    # Find partial block data size (at the end of the file this time)
                    num_bytes_partial_block_copy = file_end_loc - (num_whole_blocks_copy * block_size)
                    # Copy whole blocks
                    f.seek(0)
                    for block in range(num_whole_blocks_copy):
                        # Read block data (automatically moves position)
                        block_data = f.read(block_size)
                        # Write block to temp file
                        temp_file.write(block_data)
                    # Test for any partial block data
                    if num_bytes_partial_block_copy > 0:
                        # Read remaining data
                        partial_block_data = f.read(num_bytes_partial_block_copy)
                        # Write remaining data to temp file
                        temp_file.write(partial_block_data)
                    # Close temp file
                    temp_file.close()
            # Delete original file
            os.remove(filename)
            # Replace original with temporary file
            shutil.move(temp_file.name, filename)
            # Return the new file size
            return(file_end_loc)
    
    new_size = strip_file_blank_space("file.bin") # Defaults to 1 MiB blocks
    

    Como você pode ver, são necessárias muito mais linhas de código, mas se você está lendo isso, essas linhas não precisam ser escritas agora! De nada. :)

    Eu testei esta função usando arquivos de 4+ GB em um Raspberry Pi com 1 GB de RAM, e o processo nunca usou mais memória do que 50 MB no total. Demorou um pouco para processar, mas funcionou perfeitamente.

    Conclusão

    Ao programar, lembre-se de quantos dados você está carregando na memória a qualquer momento. Tenha em mente o maior tamanho de arquivo potencial com o qual você trabalhará e os limites inferiores potenciais da memória disponível para você.

    Espero que isso ajude alguém na linha!

    • 0
  5. Stéphane Chazelas
    2022-05-05T21:32:22+08:002022-05-05T21:32:22+08:00

    Pelo menos no Linux (e em sistemas de arquivos que o suportam, como o ext4 moderno), você pode usar fallocate -dpara substituir essa sequência de zeros por buracos que não ocupam espaço em disco:

    $ echo test > a
    $ head -c1G /dev/zero >> a
    $ echo test2 >> a
    $ head -c1G /dev/zero >> a
    $ du -h a
    2.1G    a
    $ ls -l a
    -rw-r--r-- 1 stephane stephane 2147483659 May  5 06:23 a
    

    Arquivo grande de 2GiB ocupando 2GiB de espaço em disco.

    $ fallocate -d a
    $ ls -l a
    -rw-r--r-- 1 stephane stephane 2147483659 May  5 06:23 a
    $ du -h a
    12K     a
    

    Mesmo arquivo grande de 2GiB, mas agora ocupando apenas 12KiB de espaço em disco.

    $ filefrag -v a
    Filesystem type is: ef53
    File size of a is 2147483659 (524289 blocks of 4096 bytes)
     ext:     logical_offset:        physical_offset: length:   expected: flags:
       0:        0..       0:    7504727..   7504727:      1:
       1:   262144..  262144:   48424960..  48424960:      1:    7766871: last
    a: 2 extents found
    

    Você pode remover o orifício à direita com:

    truncate -os 262145 a
    

    O último bloco agora deve conter dados:

    $ tail -c4096 a | hd
    00000000  00 00 00 00 00 74 65 73  74 32 0a 00 00 00 00 00  |.....test2......|
    00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    *
    00001000
    

    Embora você também possa remover os zeros à direita no último bloco, observe que isso não economizará espaço no disco.

    • 0
  6. Stéphane Chazelas
    2022-05-05T22:05:35+08:002022-05-05T22:05:35+08:00

    Observe que não é porque 3/4 de um sistema de arquivos não está alocado que os blocos correspondentes do dispositivo de armazenamento subjacente conterão zeros. Se alguns arquivos foram gravados antes, mas excluídos desde então, os dados antigos ainda estarão lá, apenas os blocos correspondentes serão marcados como não alocados na estrutura do sistema de arquivos.

    Uma exceção a isso pode ser se o suporte TRIM/DISCARD estiver disponível no dispositivo de bloco e for usado quando os sistemas de arquivos forem montados.

    No Linux, os loopdispositivos suportam o corte e criarão um buraco correspondente no arquivo que faz o loop se for suportado pelo sistema de arquivos subjacente, para que você possa montar o sistema de arquivos em sua imagem:

    sudo mount -o loop file.bin /somewhere
    

    E faça um:

    sudo fstrim /somewhere
    

    Para descartar os blocos não alocados do sistema de arquivos.

    Se a imagem for particionada:

    sudo losetup -fP --show file.bin
    

    Em seguida, monte a /dev/loopXpY(s) partição(ões) correspondente(s).

    Você também pode querer ver coisas como partimagefazer um dump do seu cartão SD. O que cuidará apenas do despejo dos bits alocados.

    Usar zerofreeno sdcard antes de despejar também garantiria que as partes não alocadas fossem preenchidas com zeros.

    • 0

relate perguntas

  • Remova arquivos com os menores sufixos de nome de arquivo

  • Listar arquivos classificados de acordo com a linha de conteúdo específica

  • Como saber antecipadamente se um .zip tem um diretório pai dentro

  • Como encontrar tipos de arquivos específicos e tar-los?

  • du/df e ls relatando diferentes usos de disco

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve