printf '%s' 'hello' | base64 prints aGVsbG8=, on Linux and macOS alike. printf '%s' writes the text exactly as given, with no newline after it.
printf '%s' 'hello' | base64
# aGVsbG8=
printf '%s' 'aGVsbG8=' | base64 --decode
# hello
The tool below shows the same aGVsbG8= for hello.
The echo trap
echo hello | base64
# aGVsbG8K
echo adds a newline, so 6 bytes are encoded and the result ends in K instead of =. Press Enter after hello in the tool and it shows aGVsbG8K too. echo -n drops the newline in bash and zsh, but POSIX makes -n implementation-defined, and macOS’s /bin/sh prints it as text:
/bin/sh -c 'echo -n hello | base64' # on macOS
# LW4gaGVsbG8K that's "-n hello" and a newline
GNU and macOS flags
Linux has GNU coreutils’ base64; macOS has its own, and they disagree:
- Line wrapping. GNU wraps the output at 76 characters, so a long value comes out on several lines. Pass
-w 0for one line. macOS doesn’t wrap unless you ask with-b 76. - Files. GNU takes the file name as an argument:
base64 photo.png. macOS answers that withbase64: invalid argument photo.pngand wants-i photo.png. On GNU,-imeans--ignore-garbage, a decoding option.base64 < photo.pngworks on both. - Decoding. GNU decodes with
-d; macOS takes-dor-D.--decodeworks on both.
head -c 60 /dev/zero | tr '\0' x | base64 # GNU
# eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4
# eHh4
Missing padding
Base64 without its = padding, as in base64url and JWTs, decodes differently on each system:
printf '%s' 'aGVsbG8' | base64 -d
# GNU coreutils 9.6: hello
# GNU coreutils 9.1: hello, plus "base64: invalid input" and exit status 1
# macOS 27: hel, exit status 0
macOS drops the last partial group without an error. Pad the string first:
s=aGVsbG8; while [ $(( ${#s} % 4 )) -ne 0 ]; do s="$s="; done
printf '%s' "$s" | base64 -d
# hello
URL-safe Base64
GNU has basenc --base64url, which still writes the padding. On any system, swap the two characters and strip the = (with -w 0 on GNU):
printf '%s' '<<???>>' | basenc --base64url
# PDw_Pz8-Pg==
printf '%s' '<<???>>' | base64 | tr '+/' '-_' | tr -d '='
# PDw_Pz8-Pg