Shell e imagens - Baixar, Copiar Salvar - o que tem de errado?

Usando o shell do linux, me deparei com uma situação inusitada:

Ao baixar uma imagem pelo wgete jogar para uma variável pra dentro do script, ou ao fazer uma leitura por meio do Cat, e tentar jogar pra o disco usando echo, ele quebra a imagem.

Já aconteceu isso com vocês? Qual a solução pra isso?

Exemplos.:

img=$(wget -qc “https://meusite/imagem.jpg”); echo “$img” > 0.jpg

img=$(cat 0.jpg); echo $img > 1.jpg

Consegui o resultado desejado (se entendi bem) contornando de uma outra maneira.

Primeiro, pelo que sei, o erro está em tentar manipular imagens como se fossem texto, usando comandos como echo ou armazenando o conteúdo em variáveis. Isso corrompe o arquivo, já que o shell não lida com binários dessa forma.

No meu caso, eu queria um script para baixar a imagem do dia (do Bing) e aplicar como papel de parede. Para isso, criei um script que faz tudo corretamente, sem corromper os dados. Está disponível no meu GitHub.

Em vez de usar variáveis e echo, o ideal é baixar direto para o disco com curl ou wget, assim:

curl -s "https://www.bing.com$url" -o "$destino"

Ou com wget:

wget -q "https://www.bing.com$url" -O "$destino"

No meu script, além de baixar, também aplico como wallpaper com feh e atualizo o tema do sistema com pywal. Tudo isso sem armazenar a imagem em variáveis nem usar echo.

Se quiser adaptar para outro uso, recomendo seguir essa lógica: sempre manipular arquivos de imagens diretamente no disco, evita corromper.


Se preferir postar o script completo, pode ser uma boa também.

2 curtidas

A minha intenção é a de baixar e realmente jogar pra uma variável, mas sem ter que baixar pra o disco.

Pq a intenção do script é a de, ao ler uma lista de links, fazer o dowload da imagem para uma variável, para que eu possa gerar um array com os arquivos diretamente pra que eu possa exportar pra uma tabela em lua.

A finalidade é essa tabela ser recuperada pelo Löve2d e poder ser exibida pelo programa.


O Love tem nas funções dele até a possibilidade de ler uma imagem no disco e depois poder baixar o ImageData como texto, e depois recuperar, pra ser lido posteriormente.

Só queria fazer isso de um jeito direto do site, sem baixar pra o disco. E sem ter de abrir o Love na montagem do arquivo.

Acho que será possível, mas transformando o dado binário em texto usando um algoritmo simples como o ‘base64’, que está disponível nas distribuições:

img=$(base64 0.jpg) #converte o arquivo 0 para texto base64

echo "$img" | base64 -d > 1.jpg #converte o conteúdo da variável para um arquivo
4 curtidas

A raiz aqui é imagens são dados binários, não texto normal. Assim vários comportamentos que o shell faz para te ajudar a processar texto de maneira mais conveniente (como remover as novas-linhas no final do $(), acrescentar uma no echo, etc.) ou mesmo “vacilos” de programação que surgem porque o seu uso é incomum (como truncar a variável ao surgir um caractere inválido) corrompem o arquivo.

Tem alguns truques que você pode usar aqui, como codificar o arquivo de um jeito que garante que ele será texto válido (ex: o base64 listado pelo @Deleteriumimg=$(curl -s $URL | base64) ou img=$(wget -qc $URL | bas64) vão funcionar também), ou estruturar o script de modo que os dados emitidos pelo download sejam jogados diretamente no arquivo em vez de passar por uma variável do shell.

# Aqui, o conteúdo do meu_arquivo vai ter tanto o cabeçalho dos `echo`
# quanto o conteúdo da imagem, pois eles estão agrupados com { }, e esse
# grupo como um todo tem um redirecionamento no final com `>`.
{
  echo "psicólogo: o formato JPEG não é real e não pode te machucar"
  echo "o formato JPEG:"
  wget -qc "$URL"
} > meu_arquivo

Curiosidade: ZSH (até onde eu sei) é um dos poucos shells que lidam corretamente com variáveis contendo caracteres inválidos. Esse código funciona:

#!/usr/bin/zsh
# o `; echo x` previne que caracteres concidindo com novas-linhas no final
# sejam comidos pelo `$()`; daí só eliminarmos o `x` do final antes de dar echo
# para "descorromper", e usar o `echo -n` para não acrescentarmos uma nova-linha
# ao final do arquivo.
# Fonte: https://mywiki.wooledge.org/CommandSubstitution
img=$(wget -qc "https://meusite/imagem.jpg"; echo x); echo -n "${img%x}" > 0.jpg

Mesmo assim, não recomendaria usar esse truque. Ele depende de um shell específico (a maioria dos shells trunca variáveis ao encontrar caracteres inválidos), e mesmo no ZSH, o comando tem que estar “preparado”, se não ele trunca/corrompe também.

3 curtidas

Ótimas sugestões o observações.
Consegui o que queria referente ao Shell - Obrigado.

Falto agora só encontrar o jeito certo do Love2d ler esse arquivo de tabelas pra poder projetar/desenhar na tela.

1 curtida

Para o Love2d a solução foi a seguinte função:

function img64(a) --Uses on love.load()
local a = a --pure imagedata base64 binary
local ac=love.data.newByteData(love.data.decode(“data”,“base64”, a))
local ae=love.image.newImageData(ac)
local img=love.graphics.newImage(ae)
return img
end

Uso:

function love.load()
img=img64( --[[sua imagem em base64 obtida pelo wget]] )
end
function love.draw()
love.graphics.draw(img,0,0)
end

1 curtida

Este tópico foi fechado automaticamente 3 dias depois da última resposta. Novas respostas não são mais permitidas.