base64 Command in Linux & macOS
The base64 command is built into Linux (GNU coreutils) and macOS (BSD libc). They look the same but have subtle flag differences that break scripts. Here is everything you need.
Basic encode and decode from the terminal
The base64 command reads from standard input (or a file) and writes the encoded result to standard output. The simplest way to encode a string is to pipe it in with echo:
$ echo -n "hello" | base64 aGVsbG8=
To decode, pass the -d flag (on Linux) and feed it the Base64 text. The command reverses the operation and prints the original bytes:
$ echo "aGVsbG8=" | base64 -d hello
Notice that we used echo -n when encoding but plain echo when decoding. On the encode side, the -n suppresses the trailing newline so it is not folded into the data. On the decode side, the trailing newline that echo adds is harmless — the decoder simply ignores characters that are not part of the Base64 alphabet. We cover this subtlety in detail in the echo -n gotcha section.
Run base64 --help on Linux or man base64 on macOS to confirm which implementation you have. GNU coreutils prints a long --help screen; BSD base64 does not support --help and will error.
Encode and decode files
Both implementations accept a filename as a positional argument, so you do not need to pipe with cat. To encode a binary file such as an image into a Base64 text representation:
$ base64 logo.png > logo.b64
This works equally on Linux and macOS. The > redirects the encoded output into a new file. To turn that Base64 text back into the original binary, decode it and redirect to a fresh filename so you do not clobber the source:
# Linux (GNU) $ base64 -d logo.b64 > logo-restored.png # macOS (BSD) $ base64 -D logo.b64 > logo-restored.png
You can verify a round trip succeeded by comparing checksums. If the hashes match, the encode/decode cycle was lossless:
$ shasum logo.png logo-restored.png 3a1f... logo.png 3a1f... logo-restored.png
Never decode in place over the source file. A command like base64 -d data.b64 > data.b64 truncates the file before it is read, destroying your data. Always write to a new filename.
GNU vs BSD: -d vs -D and line wrapping
This is the single most common source of "it works on my machine" failures with base64. Linux ships GNU coreutils, where the decode flag is lowercase -d (with a long form --decode). macOS ships the BSD implementation, where the decode flag is uppercase -D. They are not interchangeable:
# Linux — works $ echo "aGk=" | base64 -d hi # macOS — the SAME command fails $ echo "aGk=" | base64 -d usage: base64 ... # macOS wants the uppercase D: $ echo "aGk=" | base64 -D hi
The second major difference is line wrapping. GNU base64 wraps encoded output at 76 characters per line by default. To produce a single unbroken line — which you almost always want when embedding the result in JSON, a header, or an environment variable — use -w 0 (wrap width zero means "do not wrap"):
$ head -c 200 /dev/urandom | base64 -w 0 # one long line, no breaks
On macOS the BSD tool does not wrap output by default, and it does not understand -w at all — passing -w 0 there is an error. If you need a portable script, the safest approach is to encode normally and strip newlines yourself with tr:
# Portable: works on both GNU and BSD $ base64 file.bin | tr -d '\n'
On macOS you can install GNU coreutils via Homebrew with brew install coreutils. It provides gbase64, which behaves exactly like the Linux command (-d, -w, and all). This lets you write one script that runs the same everywhere by calling gbase64.
Using base64 in pipelines and shell variables
Because base64 reads stdin and writes stdout, it slots cleanly into pipelines. A common pattern is building an HTTP Basic Auth header, which is the colon-joined user:password string encoded as Base64:
$ printf '%s' "alice:s3cret" | base64 YWxpY2U6czNjcmV0
We use printf '%s' instead of echo here because printf never appends a newline and behaves identically across shells — making it the most reliable choice for credential encoding.
To capture encoded output in a shell variable, use command substitution. This is handy when you need the value later in the script:
$ token=$(printf '%s' "alice:s3cret" | base64) $ curl -H "Authorization: Basic $token" https://api.example.com
You can also decode straight into a variable, then act on it. For example, pulling a value out of a Kubernetes secret (which stores everything Base64-encoded) and using it inline:
$ password=$(echo "$ENCODED_SECRET" | base64 -d)
$ echo "Decoded length: ${#password}"
Command substitution with $(...) strips trailing newlines from the captured output. That is usually what you want, but be aware of it if you are decoding binary data into a variable — shell variables cannot hold NUL bytes, so always decode binary to a file, not a variable.
The echo -n gotcha
This trips up almost everyone the first time. By default, echo appends a trailing newline character (\n, byte 0x0a) to its output. Since Base64 encodes every byte it receives, that invisible newline changes the result:
$ echo "hello" | base64 aGVsbG8K # note the trailing K — that's the newline $ echo -n "hello" | base64 aGVsbG8= # correct: 5 bytes only
The difference is real and breaks things. If you encode a password or API token with a stray newline, the receiving system decodes a value that ends in \n and authentication fails — often with a confusing error that does not mention whitespace at all.
Two reliable fixes: use echo -n to suppress the newline, or use printf '%s' which never adds one. The printf form is preferable in portable scripts because some shells (notably dash and certain sh variants) treat echo -n inconsistently — they may print the literal text -n instead of honoring the flag:
# Most reliable across all shells $ printf '%s' "my-secret-token" | base64
When decoding, the opposite is true: trailing whitespace is forgiven. The decoder ignores newlines and other non-alphabet characters, so echo "aGVsbG8=" | base64 -d works fine even though echo added a newline to the Base64 input.
Common errors and fixes
Here are the failures you are most likely to hit and how to resolve each one:
base64: invalid option -- 'D'(Linux) — You used the macOS decode flag on Linux. Switch to lowercase-dor--decode.usage: base64 ...after-d(macOS) — BSD base64 does not accept-d; it needs uppercase-D. Use-D, or installcoreutilsand callgbase64 -d.base64: invalid input— The input contains characters outside the Base64 alphabet or has a wrong length. GNU base64 is strict by default. Pass-i(GNU--ignore-garbage) to skip non-alphabet bytes, or check for stray quotes and spaces.- Decoded value has an extra newline — You encoded with plain
echoinstead ofecho -norprintf '%s'. Re-encode without the trailing newline. - Long output wrapped across multiple lines — GNU wraps at 76 columns. Add
-w 0(Linux only) or pipe throughtr -d '\n'for a portable one-liner. Incorrect paddingerrors downstream — The Base64 string was truncated or had its=padding stripped. Make sure the full value, including any trailing=characters, is preserved when you copy it.
When a script must run on both Linux and macOS, the most robust pattern is to avoid the divergent decode flag entirely by detecting the platform, or by standardizing on a known implementation. A small wrapper makes scripts portable:
decode() {
if base64 --help 2>&1 | grep -q -- '--decode'; then
base64 -d # GNU
else
base64 -D # BSD / macOS
fi
}
$ echo "aGVsbG8=" | decode
hello
For a quick, zero-install round trip without worrying about flags or platforms at all, paste your string or drop a file into base64.dev — it auto-detects whether to encode or decode and handles padding and newlines for you.
FAQ
How do I base64 encode a string in Linux terminal?
Use echo -n "your string" | base64. The -n flag suppresses the trailing newline — without it, the newline is included in the encoded output.
Why does base64 -d work on Linux but not macOS?
Linux uses GNU coreutils where -d means decode. macOS uses BSD base64 where the decode flag is -D (uppercase). Use -D on macOS or -d on Linux.
How do I remove line wrapping from base64 output on Linux?
Use base64 -w 0 to disable line wrapping on Linux. On macOS, output is not wrapped by default.
Try Base64 encoding and decoding instantly
Paste any string or file — base64.dev auto-detects and converts it instantly.
Open base64.dev →