shellcheck: линтер для bash-скриптов, которые все пишут на глаз

Bash-скрипт для деплоя обычно пишется быстро и без ревью. shellcheck находит классические ошибки ещё до запуска. apt install shellcheck shellcheck deploy.sh deploy.sh:12:5: warning: Double quote to prevent globbing and word splitting [SC2086] deploy.sh:20:1: warning: This command doesn't exit, but sourced or eval'd files may have set -e [SC2317] Типичные находки: $VAR без кавычек в пути с пробелами, [ $a == $b ] вместо [ "$a" = "$b" ], забытая проверка $?. Каждое предупреждение — с кодом SCxxxx и ссылкой на объяснение. ...

3 сентября 2026 г. · llexa

diff: не только -u — сравнение файлов и директорий

Голый diff old new выводит формат, неудобный для чтения и не применимый обратно. На практике почти всегда нужны конкретные флаги. diff -u old.conf new.conf > fix.patch # unified — как git diff patch < fix.patch # применить патч diff -rq /etc/nginx/sites-available /backup/sites-available # только имена разных файлов diff -w old.conf new.conf # игнор пробелов -u — unified diff, тот же формат, что и git diff, единственный, который patch применяет без путаницы со смещением строк. -r рекурсивно по директориям, -q — не печатать содержимое, только “differ”/“only in”. -w спасает, когда разница только в отступах после автоформатера. ...

31 июля 2026 г. · llexa

bash-функции продвинутые: local, return и nameref

В bash-функциях легко наступить на грабли. Переменная без local утекает в глобальную область и портит состояние вызывающего кода. return возвращает только код завершения — 0–255, не строку и не число. Три инструмента снимают большую часть боли. count_lines() { local file="$1" n n=$(wc -l < "$file") echo "$n" # значение — через stdout, не return } check_disk() { [ "$(df -h / | awk 'NR==2{print $5}' | tr -d '%')" -lt 90 ] } fill_array() { local -n ref=$1 ref=(one two three) } my_arr=() fill_array my_arr Частая ошибка — вернуть число через return, когда оно больше 255 (обрежется по модулю 256). Для реальных данных — echo+$(...) или nameref, return только под true/false. ...

28 июля 2026 г. · llexa

Makefile с нуля: targets, .PHONY и автоматические переменные

make — не только для C. По сути это раннер задач с зависимостями: target пересобирается, только если изменился хоть один файл, от которого он зависит. Для команд без файла-результата (test, deploy, clean) нужен .PHONY — иначе make однажды решит, что цель уже “собрана”, просто увидев файл с таким же именем в директории. .PHONY: build test deploy clean build: main.o utils.o gcc -o app $^ # $^ — все зависимости main.o: main.c gcc -c $< -o $@ # $< зависимость, $@ цель test: build ./run_tests.sh deploy: test scp app prod:/opt/app/ $@/$</$^ убирают дублирование имён файлов. deploy: test гарантирует: сначала тесты, потом бинарник на прод. ...

28 июля 2026 г. · llexa

printf: форматирование вывода вместо echo

echo в разных шеллах и с флагами -e/-n ведёт себя непредсказуемо. printf — POSIX-стандарт с одинаковым поведением везде, плюс контроль, которого у echo просто нет: выравнивание, нули, точность после запятой. printf "%-10s %5d\n" "disk1" 87 # ширина и выравнивание — таблицы в логах printf "%.2f%%\n" 87.3456 # точность после запятой printf "backup_%03d.tar\n" 7 # backup_007.tar # запись в переменную без $(...) и лишнего subshell printf -v msg "Ошибка на хосте %s, код %d" "$host" "$code" %q экранирует строку так, что результат безопасно вставить обратно в shell — printf "%q\n" "$var" пригождается при генерации команд из переменных с пробелами. ...

28 июля 2026 г. · llexa

age: современная замена gpg для шифрования файлов

gpg умеет всё — подписи, web-of-trust, keyserver’ы, десятки опций — но для простой задачи «зашифровать файл получателю» это избыточная сложность. age делает ровно одну вещь и без конфигов. age-keygen -o key.txt # Public key: age1qy... Шифрование по публичному ключу получателя, можно на нескольких сразу: age -r age1qy... -o secret.age file.txt age -r age1qy... -r age1zx... -o secret.age file.txt Расшифровка — своим приватным ключом: age -d -i key.txt secret.age > file.txt Если получателя нет и нужен просто пароль — симметричный режим без ключей вообще: ...

23 июля 2026 г. · llexa

bash regex: =~ и BASH_REMATCH без внешних утилит

Для простой проверки строки необязательно звать grep отдельным процессом — bash умеет regex сам, через =~ внутри [[ ]]. line="user=admin;role=superadmin" [[ $line =~ role=([a-z]+) ]] && echo "role: ${BASH_REMATCH[1]}" BASH_REMATCH[0] — всё совпадение целиком, [1], [2]… — группы в скобках по порядку открывающих (. Массив живёт до следующего =~. Синтаксис — extended regex (ERE), как grep -E, а не basic как обычный grep: группы без экранирования, +/?/{} работают напрямую. Для разовой проверки это быстрее fork/exec внешнего процесса, но для построчной обработки файла grep -P/awk всё равно удобнее. ...

21 июля 2026 г. · llexa

expect: автоматизация того, что просит ввести пароль вручную

Некоторые старые CLI-утилиты не берут пароль из stdin — только приглашением. expect скриптует диалог: ждёт паттерн в выводе и посылает ответ. #!/usr/bin/expect spawn passwd testuser expect "New password:" send "S3cur3Pass!\r" expect "Retype new password:" send "S3cur3Pass!\r" expect eof spawn запускает процесс, expect "паттерн" ждёт его в выводе (regex), send пишет в stdin, expect eof — дожидается завершения (без него скрипт выйдет раньше процесса). autoexpect записывает сессию как script, сразу генерируя .exp-файл — писать с нуля не обязательно. expect — крайняя мера для legacy без non-interactive режима; где есть --batch/-y, лучше они. ...

21 июля 2026 г. · llexa

logger и systemd-cat: пишем в journald из bash

Скрипт, пишущий статус в свой файл в /var/log/, — ещё одна точка для ротации и мониторинга. logger шлёт сообщение прямо в syslog/journald, под общую ротацию и journalctl. logger -t backup-script -p user.err "disk full" # тег + приоритет journalctl -t backup-script # только свои сообщения systemd-cat оборачивает целую команду — её stdout/stderr: systemd-cat -t rsync-job rsync -av /data /backup/ journalctl -t rsync-job --since "10 min ago" Из cron: job.sh 2>&1 | logger -t rsync-job — результат ищется одной командой, без grep по файлам в /var/log/. Приоритет (err, warning) фильтрует по важности и вешает алерты на ошибки, не трогая шум. ...

20 июля 2026 г. · llexa

awk: BEGIN/END, массивы и функции — не только '{print $1}'

awk '{print $2}' знают все, но за этим стоит язык программирования с блоками инициализации, накопительными переменными между строками и своими функциями. awk 'BEGIN{print "start"} {n++} END{print "total:", n}' file — счётчик строк с сообщениями до и после обработки awk '{sum[$1]+=$2} END{for (k in sum) print k, sum[k]}' file — суммировать вторую колонку по группам из первой, аналог GROUP BY awk 'function sq(x){return x*x} {print sq($1)}' file — своя функция внутри awk-программы awk -F: '{print $1}' /etc/passwd — свой разделитель полей вместо пробела awk 'NR==FNR{a[$1]=$2; next} $1 in a{print $0, a[$1]}' ref.txt data.txt — join двух файлов по первому полю #linux #bash

14 июля 2026 г. · llexa