Enviar um arquivo
  1. base64hub
  2. Base64 no código
  3. Bash
Código

Base64 no Bash

Quem faz o trabalho é o comando base64; o Bash só passa os dados para ele. O GNU coreutils no Linux e a ferramenta BSD no macOS usam flags diferentes. As saídas de Linux abaixo vêm do GNU coreutils 9.7.

Codificar e decodificar uma string

printf '%s' 'héllo wörld' | base64          # aMOpbGxvIHfDtnJsZA==
printf '%s' 'aMOpbGxvIHfDtnJsZA==' | base64 -d   # héllo wörld

O base64 codifica bytes, então o resultado segue a codificação do seu terminal. Em qualquer Linux ou macOS moderno, isso é UTF-8.

O erro clássico é o echo, que acrescenta uma quebra de linha:

echo 'héllo wörld' | base64      # aMOpbGxvIHfDtnJsZAo=   (note the "o=")
echo -n hi | base64              # aGk=

O echo -n funciona no Bash, mas não em todo sh; o printf '%s' é portável. Uma here-string (base64 <<< "text") também acrescenta quebra de linha. Se alguém lhe mandar Base64 terminado em Cg== ou o=, provavelmente há uma quebra de linha no final; cole no decodificador Base64 para conferir.

Flags do GNU (Linux) vs BSD (macOS)

TarefaGNU coreutils (Linux)macOS
Decodificar-d, --decode-D, --decode (versões recentes também -d)
Sem quebra de linha-w 0padrão
Quebrar a cada N caracteres-w N (padrão 76)-b N
Arquivo de entradabase64 filebase64 -i file ou base64 < file
Arquivo de saídaredirecionar com >-o file ou redirecionar
Ignorar lixo ao decodificar-i, --ignore-garbagenão disponível

As duas flags -i significam coisas diferentes: no macOS ela indica o arquivo de entrada, no Linux ela ignora lixo. Scripts portáveis devem ler da stdin e escrever na stdout:

base64 < in.bin | tr -d '\n' > out.b64     # same output on Linux and macOS
base64 --decode < out.b64 > in-copy.bin

O base64 do GNU quebra em 76 caracteres por padrão, o que estraga headers e variáveis de ambiente:

head -c 60 /dev/zero | tr '\0' x | base64 -w 0
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4

Sem -w 0, a primeira linha tem 76 caracteres e o resto vai para uma segunda linha. Se você precisa de linhas no estilo MIME, a quebra padrão é exatamente o que quer.

Base64 URL-safe

O GNU coreutils 8.31 e mais novos incluem o basenc, que suporta Base64URL. Ele mantém o preenchimento:

printf '%s' '<<???>>' | basenc --base64url      # PDw_Pz8-Pg==
printf '%s' 'PDw_Pz8-Pg' | basenc --base64url -d   # <<???>>

No macOS ou em Linux mais antigo, traduza com tr:

printf '%s' '<<???>>' | base64 | tr '+/' '-_' | tr -d '='   # PDw_Pz8-Pg

Para decodificar, é preciso fazer a tradução inversa e, para decodificadores rígidos, recolocar o preenchimento:

s=PDw_Pz8-Pg
s=$(printf '%s' "$s" | tr -- '-_' '+/')
while [ $(( ${#s} % 4 )) -ne 0 ]; do s="$s="; done
printf '%s' "$s" | base64 -d        # <<???>>

O -- impede que o tr leia -_ como uma opção. É a mesma transformação de que os JWTs precisam; o decodificador de JWT faz isso por você. Mais em Base64 vs Base64URL.

Codificar e decodificar arquivos

base64 -w 0 hello.txt              # aGVsbG8K
base64 -d <<< 'aGVsbG8K' > copy.txt
base64 -d payload.b64 > payload.pdf

O base64 trabalha em fluxo, então o tamanho do arquivo não importa; ele lê e escreve em blocos. Para montar uma data URI de uma imagem:

printf 'data:image/png;base64,%s' "$(base64 -w 0 logo.png)"

Para um header HTTP Basic Auth:

printf 'Basic %s' "$(printf '%s' 'user:pass' | base64)"   # Basic dXNlcjpwYXNz

O gerador de header Basic Auth faz o mesmo sem deixar a sua senha no histórico do shell.

Validação, erros e códigos de saída

O base64 -d do GNU sai com status 1 e imprime base64: invalid input na stderr quando encontra um caractere inválido. Mesmo assim, ele escreve tudo o que decodificou até ali, então uma decodificação que falha pode deixar um arquivo parcial para trás:

printf 'aGk=!!' | base64 -d        # prints "hi", then: base64: invalid input
echo $?                            # 1 (without a pipe after it)
printf 'aGk=!!' | base64 -d -i     # hi   (garbage ignored, exit 0)

Resultados com o coreutils 9.7:

EntradaResultado
aGkhi, exit 0 (preenchimento ausente tolerado)
"aGVs\nbG8K"hello, exit 0 (quebras de linha ignoradas)
aGl=hi, depois invalid input, exit 1
ainvalid input, exit 1
PDw_Pz8-Pg==saída parcial, invalid input, exit 1

Use o status de saída para validar e decodifique para um arquivo temporário, assim uma falha não sobrescreve dados bons:

if printf '%s' "$b64" | base64 -d > /tmp/out.bin 2>/dev/null; then
  mv /tmp/out.bin result.bin
else
  echo "invalid Base64" >&2
fi

Num pipeline, $? é o status do último comando. Acrescente set -o pipefail se o base64 -d não for o último. Quando uma string tirada de um log ou de um e-mail não decodifica, o Reparar Base64 remove aspas, espaços e prefixos; o guia de erros explica cada falha.

Outras ferramentas de linha de comando

  • O openssl base64 existe em quase todo sistema e se comporta igual no Linux e no macOS. Veja Base64 com OpenSSL.
  • python3 -m base64 -d file e python3 -m base64 file funcionam onde o coreutils não está disponível; a saída quebra em 76 caracteres.
  • No Kubernetes, o padrão é kubectl get secret x -o jsonpath='{.data.password}' | base64 -d, ou use o decodificador de secrets do Kubernetes.

Perguntas frequentes

Por que echo hello | base64 dá um resultado diferente das ferramentas online?

O echo acrescenta uma quebra de linha, então você codifica hello\n (aGVsbG8K) em vez de hello (aGVsbG8=). Use printf '%s' hello ou echo -n hello.

Como impedir que o base64 quebre as linhas?

No Linux, base64 -w 0. O macOS não quebra por padrão. Para um comando que se comporte igual nos dois, passe a saída por tr -d '\n'.

O que significa 'base64: invalid input'?

O base64 -d do GNU encontrou um caractere fora do alfabeto ou um preenchimento quebrado. Ele imprime os bytes que decodificou até ali, depois o erro, e sai com status 1. Os culpados comuns são - e _ do URL-safe, aspas e prefixos data:.

O base64 -d no macOS é igual ao do Linux?

Versões antigas do macOS só conheciam -D (ou --decode); as recentes também aceitam -d. Use --decode em scripts que precisam rodar nos dois.

Continue lendo