Linux. Кодирование/декодирование файлов в/из BASE64

Преамбула

Как однажды сказал мой учитель, «линукс может все, а многое еще и из коробки, надо только знать как». Вот в данном случае именно так и оказалось. Понадобилось мне массово перекодировать файлы в/из BASE64.

BASE64 это такой древний формат данных, использующийся для передачи двоичных данных по сети. В век всеобщего юникода и кучи всякого софта это не так видно пользователю, но когда все только начиналось, с передачей данных была большая жопа. Даже на «железном» уровне некоторые машины поддерживали только семибитную кодировку, а уж о софте, не поддерживающем ничего кроме великого и могучего английского языка и говорить не приходится. Т.е. любой символ кроме букв английского алфавита и цифр мог быть либо выкинут к чертям собачьим, либо невозбранно использован как служебный. Соответственно, любой бинарный файл мог каким либо софтом, например, хитровыебанным почтовым сервером, разорван в клочья при передаче из пункта А в пункт Б.

Вот и придумали умные люди, а давайте все, что не влазит в английский алфавит и цифры, будем по хитрому алгоритму заменять, таки да. На английский алфавит и цифры. Так и получился BASE64. Подобная хрень в криптографии называется «транспортная броня».

Так вот, в Linux из коробки (входит в пакет Coreutils, который есть почти везде) есть утилита, способная кодировать/декодировать BASE64. Называется она, внезапно, base64.

Кодирование

Для опытов возьмем восьмибитного Сиро Исии. Он, когда живой был, много кого брал на опыты.

base64 isia.png >isia.b64

Закодированный Сиро Исии на Pastebin

Декодирование

base64 -d isia.b64 >isia.decoded.png

Тот случай, когда суть получилась короче преамбулы.

Аналог trim() в bash/linux shell

Преамбула

Пишу небольшой скрипт, для которого надо сохранять две выбранные пользователем при предыдущих запусках скрипта опции, а при следующих запусках их восстанавливать.
Решил воспользоваться самым простым методом. Создаем рядом со скриптом 2 файла, и туда сохраняем значения опций:

echo "$OPT1" > ./option1
echo "$OPT2" > ./option2

Некрасиво, но просто.

При запуске скрипта, читаем опции обратно:

OPT1=`cat ./option1`
OPT2=`cat ./option2`

На всякий случай лучше обрезать лишние пробелы или «пустые» символы типа TAB или переводов строки. Во всяком случае, так рекомендуют во всех руководствах по программированию, и обычно, в каждом современном языке, в некотором виде есть функция trim(), которая обрезает начальные и конечные пробелы.

Оказывается, что в bash можно обрезать не только начальные пробелы, но даже начальные и конечные пустые строки. Причем решается это одной командой.

Тестовый файл и вывод его в консоль

Возьмем, например, такой тестовый файл: На PasteBin

Если мы просто выведем его на консоль с помощью команды cat "trimtext.txt", то получится следующее:

smallwolfie@wolfschanze:~/myfiles/test/trim# ./testtrim


                   Trim()

smallwolfie@wolfschanze:~/myfiles/test/trim#

т.е. со всеми пробелами и пустыми строками.

Решение

Самая простая реализация trim() в shell/bash пропустить вывод команды через xargs без параметров. xargs, которая требует после себя команды, без указания команды все равно что echo без параметров. А echo без параметров все пустое обрезает.

#!/bin/bash

cat "trimtext.txt"|xargs

Вывод:

./testtrim
Trim()

Linux dialog, прикручиваем симпатичный progressbar к wget.

Преамбула

Какой бы ваш скрипт не был диалоговым, «само дерево жужжать не может», а в скрипте обычно жужжат утилиты, которые о диалоговых окнах представления не имеют. Так вот прикрутим контролируемый прогрессбар (--gauge) из виджетов dialog к всем известному wget.

Готовый код

#!/bin/bash

FADDR="http://tolik-punkoff.com/static/test.mp3"

wget --progress=dot -O "./test.mp3" "$FADDR" 2>&1 |\
stdbuf -o0 awk '/[.] +[0-9][0-9]?[0-9]?%/ { print substr($0,63,3) }' | \
dialog --gauge "Download file from $FADDR" 10 100

Разбираем, что он делает

1. Если просто запустить wget без параметра --progress=dot (параметр -O "./test.mp3" здесь показывает путь и имя для сохраняемого файла, для теста оно у нас жестко задано), то wget попытается нарисовать свой плохенький progressbar:

Использование параметра --progress=dot приводит вывод на экран в такой вид:

Теперь у нас видны проценты, которые выводятся по штуке на строчку через определенный интервал времени по внутреннему алгоритму wget.

2. wget почему-то выводит сообщения о своей работе не на stdout, а на stderr. Аж морово поветрие какое-то, ибо и некоторые другие утилиты тоже выводят на stderr. Оправданно на stderr выводит сообщения только dialog, тому ще он всю псевдографику выводит на stdout. Отучаем wget писать на stderr вместо stdout:

2>&1

2 — зарезервированный канал для stderr 1 — для stdout. Тут мы просто перенаправляем один канал в другой.

3. Меняем буферизацию. На самом деле, буфер консоли (терминала) выводит на экран сообщение, если в буфере консоли накопилось 1024 байта. Или программа завершилась. Иначе нет. С помощью утилиты stdbuf устанавливаем буфер stdout (-o) в ноль (-o0), тем самым мы добиваемся, чтоб символы сразу поступали на вход awk. stdbuf в качестве второго параметра требует программу, которой она будет посылать измененный буфер. В данном случае awk.

buffering in standard streams
man stdbuf на русском

За разъяснение спасибо другу из Телеграм.

4. Далее вывод wget‘а передается awk, которая хитрой регуляркой и оператором substr вырезает из вывода все, кроме значений процентов.

5. dialog --gauge может принимать из stdout числа, и отображать их в виде progressbar’а

Пример на GitHub

Linux. Определить в скрипте, что программа не найдена. И другие зарезервированные коды завершения.

Преамбула

Понадобилось мне перед запуском скрипта определить, имеется ли в пользовательской системе утилита, которая вызывается в скрипте, и если ее таки нет, сказать юзеру, мол, поставьте, вот ссылка на скачивание.

Решение

Оказывается в Linux есть стандартные (зарезервированные) коды завершения, а если программа не найдена, то bash автоматически выдает код завершения 127. Таким образом, задача сводится к тому, чтобы на код 127 правильно отреагировать.

Можете проверить на этом скрипте:

#!/bin/bash

$1

EXITCODE=$?

if [ $EXITCODE -eq 127 ]; then
    echo "Command not found!"
else
    echo "Exit code: $EXITCODE"
fi

Проверка

/excode-test ls
excode-test pb01 pb02 test.mp3
Exit code: 0

Команда существует и выполнена успешно.

./excode-test sagfhjsdgfhjgf
./excode-test: line 3: sagfhjsdgfhjgf: command not found
Command not found!

Примечание: Переменная $?, содержащая код завершения, будет переопределена любой следующей командой, потому можно взять за правило, сохранять значение $? в отдельную переменную, если где-то потом в скрипте понадобится проверка кода завершения.

Другие зарезервированные коды завершения

1 — разнообразные ошибки, используется как стандартный код ошибки в скриптах или программах
2 — согласно документации к Bash — неверное использование встроенных команд, иногда программисты его перелпределяют.
126 — вызываемая команда не может быть выполнена, возникает из-за проблем с правами доступа или когда вызван на исполнение неисполняемый файл
127 — «команда не найдена»
128 — неверный аргумент команды exit
128+n — фатальная ошибка по сигналу «n»
130 — завершение по Control-C
255 — код завершения вне допустимого диапазона, но утилита dialog использует этот код, как завершение по ESC.

Источник

Коды завершения, имеющие предопределенный смысл