Загрузить файл
  1. base64hub
  2. Base64 в коде
  3. Bash
Код

Base64 в Bash

Всю работу делает команда base64, Bash лишь передаёт ей данные. У GNU coreutils в Linux и у BSD-утилиты в macOS разные флаги. Результаты для Linux ниже получены на GNU coreutils 9.7.

Закодировать и декодировать строку

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

base64 кодирует байты, поэтому результат зависит от кодировки терминала. В любой современной Linux или macOS это UTF-8.

Чаще всего подводит echo: он дописывает перевод строки.

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

echo -n работает в Bash, но не в каждом sh; printf '%s' переносим. Here-string (base64 <<< "text") тоже добавляет перевод строки. Если вам прислали Base64, который заканчивается на Cg== или o=, скорее всего, в конце есть перевод строки. Вставьте строку в декодер Base64 и проверьте.

Флаги GNU (Linux) и BSD (macOS)

ЗадачаGNU coreutils (Linux)macOS
Декодировать-d, --decode-D, --decode (в новых версиях также -d)
Без переноса строк-w 0по умолчанию
Перенос через N символов-w N (по умолчанию 76)-b N
Входной файлbase64 filebase64 -i file или base64 < file
Выходной файлперенаправление >-o file или перенаправление
Пропускать мусор при декодировании-i, --ignore-garbageнет

Два флага -i значат разное: в macOS это входной файл, в Linux – пропуск мусора. Переносимые скрипты должны читать из stdin и писать в stdout:

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

GNU base64 по умолчанию переносит строки через 76 символов, а это ломает заголовки и переменные окружения:

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

Без -w 0 первая строка будет длиной 76 символов, а остаток уйдёт на вторую. Если вам нужны строки в стиле MIME, перенос по умолчанию вам как раз подходит.

URL-safe Base64

В GNU coreutils 8.31 и новее есть basenc, который поддерживает Base64URL. Дополнение он сохраняет:

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

В macOS или старых Linux замените символы через tr:

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

Для декодирования нужна обратная замена, а строгим декодерам – ещё и возвращённое дополнение:

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

-- не даёт tr принять -_ за опцию. Точно такое же преобразование нужно для JWT; декодер JWT сделает его за вас. Подробнее в статье Base64 и Base64URL.

Кодирование и декодирование файлов

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

base64 читает и пишет блоками, поэтому размер файла не важен. Собрать data URI для картинки можно так:

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

Заголовок HTTP Basic Auth:

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

Генератор заголовка Basic Auth делает то же самое, но ваш пароль не остаётся в истории команд.

Проверка, ошибки и коды выхода

Найдя недопустимый символ, GNU base64 -d завершается с кодом 1 и пишет в stderr base64: invalid input. При этом всё, что успел декодировать, он выводит, и после неудачного декодирования может остаться недописанный файл:

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)

Результаты на coreutils 9.7:

ВходРезультат
aGkhi, код 0 (отсутствие дополнения допускается)
"aGVs\nbG8K"hello, код 0 (переводы строк игнорируются)
aGl=hi, затем invalid input, код 1
ainvalid input, код 1
PDw_Pz8-Pg==часть результата, invalid input, код 1

Проверяйте код выхода и декодируйте во временный файл, чтобы ошибка не затёрла нормальные данные:

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

В конвейере $? – это код последней команды. Если base64 -d стоит не последней, добавьте set -o pipefail. Когда строка из лога или письма не декодируется, починка Base64 уберёт кавычки, пробелы и префиксы, а в статье об ошибках разобран каждый случай.

Другие утилиты командной строки

  • openssl base64 есть почти в любой системе и ведёт себя одинаково в Linux и macOS. См. Base64 в OpenSSL.
  • python3 -m base64 -d file и python3 -m base64 file работают там, где нет coreutils; вывод переносится через 76 символов.
  • В Kubernetes обычно пишут kubectl get secret x -o jsonpath='{.data.password}' | base64 -d, а можно воспользоваться декодером секретов Kubernetes.

Вопросы и ответы

Почему echo hello | base64 даёт не тот результат, что онлайн-инструменты?

echo добавляет перевод строки, поэтому кодируется hello\n (aGVsbG8K), а не hello (aGVsbG8=). Используйте printf '%s' hello или echo -n hello.

Как запретить base64 переносить строки?

В Linux – base64 -w 0. В macOS строки по умолчанию не переносятся. Чтобы команда вела себя одинаково в обеих системах, пропустите вывод через tr -d '\n'.

Что значит «base64: invalid input»?

GNU base64 -d встретил символ вне алфавита или испорченное дополнение. Он выводит байты, которые успел декодировать, затем ошибку и завершается с кодом 1. Чаще всего виноваты URL-safe символы - и _, кавычки и префиксы data:.

base64 -d в macOS работает так же, как в Linux?

Старые версии macOS знали только -D (или --decode), новые принимают и -d. В скриптах, которые должны работать в обеих системах, пишите --decode.

Читайте также