Base64 en Bash: el comando base64, el lío de macOS y los pipes
Bash no tiene un Base64 builtin, pero cualquier Unix donde corras trae el comando `base64`. La trampa es que GNU coreutils y BSD (macOS) hablan flags ligeramente distintas, y un newline perdido te arruina la tarde.
El round-trip básico
Codificar texto desde un string es un pipe:
echo -n "AldeaCode" | base64
# QWxkZWFDb2Rl
El -n en echo es crítico. Sin él, echo añade un salto de línea y codificas AldeaCode\n en lugar de AldeaCode. El resultado no coincide con lo que produciría un snippet de Python o Node, y te pasas veinte minutos depurando un misterio "tengo un byte de más".
Decodificar va con -d:
echo "QWxkZWFDb2Rl" | base64 -d
# AldeaCode
base64 -d ignora whitespace al final, así que un echo normal funciona en la decodificación. Esa asimetría es la raíz de mil hilos en StackOverflow.
BSD de macOS vs GNU coreutils
En Linux, base64 de GNU coreutils mete saltos cada 76 caracteres por defecto y acepta -w 0 para desactivar el wrap. En macOS, base64 es la implementación BSD y usa -b 0 para lo mismo. El flag -w se ignora silenciosamente en macOS, así que el script que "funciona en mi máquina" emite un string distinto en CI.
Solución portable: pipe a tr -d '\n' y te cargas todos los newlines independientemente de quién los meta.
echo -n "AldeaCode" | base64 | tr -d '\n'
Esta es la forma para meter en un Makefile, un pipeline de CI o cualquier cosa que corra en más de un sistema operativo. Más fea, a prueba de balas.
Codificar archivos y binarios
Para archivos, pasa la ruta directamente a base64:
base64 logo.png > logo.b64
base64 -d logo.b64 > logo.png
La redirección < también funciona, pero el argumento posicional es más corto y se lee mejor.
Un uso real bastante común: construir una cabecera Basic auth para un curl puntual:
AUTH=$(echo -n "user:p@ssw0rd" | base64)
curl -H "Authorization: Basic $AUTH" https://api.example.com
Examen sorpresa: si te olvidas del -n aquí, las credenciales resultantes llevan un newline embebido y la API te devuelve un 401. Bienvenido al debug de Basic auth.
Variante URL-safe a mano
base64 en Bash no trae modo URL-safe nativo. Lo construyes con tr:
echo -n "AldeaCode" | base64 | tr '+/' '-_' | tr -d '='
Base64 estándar a URL-safe es exactamente esa sustitución: + se vuelve -, / se vuelve _ y quitas el padding. Para decodificar al revés, deshaces la sustitución, rellenas a múltiplo de 4 con = y pasas a base64 -d. La mayoría de formatos de token (JWT en particular) siguen exactamente esta convención, codificada en RFC 4648 §5.
Ejemplo completo
bash#!/usr/bin/env bash
set -euo pipefail
# Codificar (portable, sin newline final)
ENCODED=$(echo -n "AldeaCode" | base64 | tr -d '\n')
echo "$ENCODED"
# QWxkZWFDb2Rl
# Decodificar
echo "$ENCODED" | base64 -d
# AldeaCode
# URL-safe, sin padding
URLSAFE=$(echo -n "AldeaCode" | base64 | tr '+/' '-_' | tr -d '=')
echo "$URLSAFE"
# Codificar un archivo
base64 logo.png > logo.b64 ¿Solo necesitas el resultado?
Cuando solo necesitas un Base64 de un string y no sabes si estás en GNU o BSD, la herramienta más cómoda es la que ni se entera. Pega el texto en el codificador Base64 del navegador, copia el resultado, sigue con tu vida.
Abrir Codificador y Decodificador Base64 →Preguntas frecuentes
¿Por qué mi Base64 no coincide con el que da Python?
Casi seguro porque echo añadió un newline. Usa echo -n o printf '%s' '...' para evitar el byte de más. Compara primero las longitudes; la salida codificada va a diferir exactamente en 4 caracteres.
¿Puedo evitar la diferencia BSD vs GNU del todo?
Sí: pipe a tr para quitar saltos de línea, y usa la forma larga del flag decode (que las dos implementaciones soportan). O instala coreutils en macOS vía Homebrew y llama a gbase64.
¿Base64 deja secretos en el historial de shell?
Sí. Cualquier cosa que pases como texto plano queda en el archivo de history. Para secretos reales, lee de un archivo, una variable de entorno cargada desde un vault, o por stdin con heredoc. Nunca pegues una contraseña de producción en la línea de comandos.