O poder do pipe e a importância de programas de linha de comando que sabem usar entrada/saída padrão

A interface de linha de comando, na marioria daz vezes, causa medo nas pessoas. Não sei se é pela aparência meio bruta ou se é pelo fato de raramente você usar o mouse (sim, tem gente que não consegue imaginar um computador útil sem um mouse!!). O fato é que apesar da aparência a linha de comando possui um poder quase infinito.

O grande poder dessa interface está na possibilidade de combinar quaisquer comandos para que seja possível executar uma tarefa maior. Nesse post proponho um desafio para um problema que parece complicado. Imagine um arquivo de 10GB compactado e dividido em 5 partes de 2GB. Como gravar esse arquivo, já descompactado, em um disco que só tem exatos 10GB de espaço? Com a linha de comando é possível!

O situação que motivou esse post foi quando precisei gravar um arquivo com mais de 6GB em meu pendrive. A maioria dos pendrives (o meu inclusive) está formatado com sistema de arquivos vfat, que não consegue gravar arquivos com mais 4GB. Então a solução foi quebrar esse arquivo em partes menores e fiz isso usando o comando split (parte do pacote GNU coreutils). Com isso consegui gravar todo o arquivo no pendrive.

As partes geradas pelo split não possuem nenhum formato específico pois ele simplesmente trunca o arquivo em partes fixas. Para juntar basta usar o cat e direcionar a saída para um arquivo qualquer, por exemplo:

split -d -b 1GB arquivo-grande.tar.bz2

Isso produz inumeras partes (de no máximo 1GB) nomeadas em sequência:  x01, x02, x03… até que todo o arquivo tenha sido repartido. Para reconstruir o arquivo original basta fazer:

cat x* > /caminho/para/o/novo/arquivo

Pois bem. O problema começou quando descobri que só tinha 8GB livres em meu disco. E para piorar eu não tinha como liberar espaço de forma rápida, então precisava dar um jeito de transferir as partes que estavam no pendrive para o computador, mas como resultado final eu teria que ter o arquivo já descompactado, afinal não teria espaço suficiente para juntar as partes e só depois descompactar!

O que proponho a você nesse post é que pense nessa solução. Como você faria essa cópia? Para ficar ainda mais divertido vamos definir algumas regras:

  • Não podemos liberar espaço nem no PC nem no pendrive;
  • Não podemos usar nem o PC nem o pendrive como área para arquivos temporários;
  • O arquivo original está no formato tar.bz2, ou seja, foi agrupado usando tar depois compatcado usando bzip2;

Para que você possa resolver esse problema de uma forma mais realista vamos simular a situação descrita acima. Para  isso vamos criar dois arquivos de 50MB (que vamos juntar com tar e depois compactar) e uma unidade de disco de 120MB, que fará o papel do nosso HD cheio. Então vamos lá:

Lembre-se de rodar todos os comando de dentro da pasta /tmp. Faça um cd /tmp antes de começar.

Para criar os dois arquivo usaremos o dd:

dd if=/dev/urandom of=/tmp/arquivo-a.bin bs=1M count=50

dd if=/dev/urandom of=/tmp/arquivo-a.bin bs=1M count=50

Nesse momento temos dois arquivos de conteúdo randômico em nosso /tmp. Agora precisamos criar o arquivo que fará o papel de disco.

dd if=/dev/zero of=/tmp/disco bs=1M count=120

Assim temos um disco de 120MB, que cabe quase que exatamente os dois arquivos de 50MB que acabamos de criar. Para que ele funcione como um disco precisamos formatá-lo:

mke2fs -j /tmp/disco

Isso faz com que ele fique no formato ext3. Para montar, execute como root:

mount -o loop /tmp/disco /mnt/disco-cheio

Nota 1: Para que esse mount dê certo, seu linux deve ter o módulo loop carregado. Caso dê algum erro no momento de montar tente carregar o módulo com o comando: modprobe loop

Nota 2: Como montamos com permisões de root, caso queira rodar os comando como usuário normal deverá mudar as permissões com o comando: chown -R seu-usuario /mnt/disco-cheio

Nesse caso montamos o disco no ponto de montagem /mnt/disco-cheio, mas poderia ser em qualquer diretório. Lembre-se apenas que antes de montar você deve cria-lo.

Agora precisamos agrupar (tar), compactar (bzip2) e dividir (split) os arquivos:

tar cvf /tmp/arquivos.tar /tmp/arquivo-a.bin /tmp/arquivo-b.bin

bzip2 /tmp/arquivos.tar

split -d -b 10M /tmp/arquivos.tar.bz2

Agora é hora de pensar! O importate é ter no final os dois arquivos originais (arquivo-a.bin e arquivo-b.bin) gravados em /mnt/disco-cheio. Para conferir se tudo está certo você pode calcular o md5 dos dois arquivos originais e comparar com o md5 dos arquivos finais, que estarão em /mnt/disco-cheio.

Pense um pouco e em um próximo post vou escrever sobre a solução que apliquei para resolver o problema.

,

  1. #1 por Flávio Coutinho em 29/09/2010 - 21:52

    Piece of cake: “cat x* | bzip2 -d | tar xv -C /mnt/disco-cheio/”

    • #2 por daltonmatos em 29/09/2010 - 22:04

      Boa! É uma forma. Existem outras variações para essa solução, inclusive com menos pipes. Vamos ver se aparecem outras soluções!

      • #3 por Flávio Coutinho em 29/09/2010 - 23:01

        Acho que um “bzcat” diminuiria o número de pipes.

  2. #4 por Thiago da Cruz em 01/10/2010 - 12:31

    Então… não é uma solução melhor, mas eu – tentando solucionar o problema – descobri uma coisa legal. Eu não conhecia esse tal de mkfifo que permite a criação de um “named pipe” ou “FIFO”.
    Assim sendo, eu solucionaria o problema com essa linha: mkfifo newPipe; cat x* > newPipe & tar -xvjf newPipe -C /mnt/disco-cheio/

    ;)

    • #5 por daltonmatos em 01/10/2010 - 13:47

      Isso é ótimo! Pois agora além de você conhecer uma solução mais compacta (a que usa somente pipes) você aprendeu uma coisa nova: Named Pipes! É isso aí!

  1. A Importância de programas que sabem usar a entrada/saída padrão: Solução do desafio « ~ #_

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: