Bash Days | Linux | DevOps
Авторский блог от действующего девопса Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу. Автор: Роман Шубин Реклама: @maxgrue MAX: https://max.ru/bashdays Курс: @tormozilla_bot Блог: https://bashdays.ru
Ko'proq ko'rsatish📈 Telegram kanali Bash Days | Linux | DevOps analitikasi
Bash Days | Linux | DevOps (@bashdays) Rus til segmentidagi kanali faol ishtirokchi. Hozirda hamjamiyat 23 788 obunachidan iborat bo'lib, Texnologiyalar & Aralashmalar toifasida 5 702-o'rinni va Rossiya mintaqasida 28 099-o'rinni egallagan.
📊 Auditoriya ko‘rsatkichlari va dinamika
невідомо sanasidan buyon loyiha tez o‘sib, 23 788 obunachiga ega bo‘ldi.
19 Iyun, 2026 dagi oxirgi ma’lumotlarga ko‘ra kanal barqaror faollikka ega. Oxirgi 30 kunda obunachilar soni -226 ga, so‘nggi 24 soatda esa 1 ga o‘zgardi va umumiy qamrov yuqori darajada qolmoqda.
- Tasdiqlash holati: Tasdiqlanmagan
- Jalb etish (ER): Auditoriya o‘rtacha 23.40% darajada jalb etiladi. Nashrdan keyingi dastlabki 24 soatda kontent odatda umumiy obunachilar sonining 13.11% ini tashkil etuvchi reaksiyalarni to‘playdi.
- Post qamrovi: Har bir post o‘rtacha 5 567 marta ko‘riladi; birinchi sutkada odatda 3 119 ta ko‘rish yig‘iladi.
- Reaksiyalar va o‘zaro ta’sir: Auditoriya faol: har bir postga o‘rtacha 22 ta reaksiya keladi.
- Tematik yo‘nalishlar: Kontent bashdays, linux, bash, docker, скрипт kabi asosiy mavzularga jamlangan.
📝 Tavsif va kontent siyosati
Muallif resursni shaxsiy fikrni ifoda etish maydoni sifatida ta’riflaydi:
“Авторский блог от действующего девопса
Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.
Автор: Роман Шубин
Реклама: @maxgrue
MAX: https://max.ru/bashdays
Курс: @tormozilla_bot
Блог: https://bashdays.r...”
Yuqori yangilanish chastotasi (oxirgi ma’lumot 20 Iyun, 2026 da olingan) sababli kanal doimo dolzarb va katta qamrovli bo‘lib qoladi. Analitika auditoriya kontent bilan faol hamkorlik qilishini, uni Texnologiyalar & Aralashmalar toifasidagi muhim ta’sir nuqtasiga aylantirishini ko‘rsatadi.
bash: VAR: readonly variable
Буквально сегодня ебался со скриптом уволившегося коллеги и не мог понять, какого лешего я получаю неочевидные результаты.
Оказалось, всё очень даже очевидно. Этот скуфяра мало того что применил ООП в скриптах (это пиздец), так еще и воспользовался «readonly». Спасибо тебе дорогой друг. Надеюсь на новом месте, тебе такого с кубернейтсами не позволяют делать. Диверсант блядь.
Еще раз, НЕ НУЖНО УСЛОЖНЯТЬ, ООП и выкрутасы оставь для программистов.Ладно. Теперь про «readonly». Если не можем победить, значит нужно изучить и победить. Это как в анекдоте 18+: Я ебу все что движется, а всё что не движется, я двигаю и ебу. Поехали двигать! Переменная защищается так:
readonly VAR=1234
Если её попытаться переопределить, то моментально отправляешься в пешее эротическое. Возникает логичный вопрос — как её откатить обратно и сделать нормальной переменной?
В голову приходит логичный ответ, сделай так:
unset VAR
А вот и нет! Сразу получишь bash: unset: VAR: cannot unset: readonly variable.
Хм… интересное кино. А чо делать? Не использовать «readonly»! Ну а если пришлось с этим столкнутся, придется использовать древнюю магию. Мы её кстати недавно использовали в этом посте.
Чтобы заансетить константу, открываем «Ящик Пандоры»:
gdb -ex 'call (int) unbind_variable("VAR")' --pid=$$ --batch
Если получил $1 = 0, значит всё прошло успешно.
Такими консервированными переменными хорошо манипулировать во всяких конфигах, например в .bashrc и т.п. Если кто-то захочет что-то переопределить, у него ничего не получится. Защита от дурака. Но помни, что древняя магия gdb может не сработать на этом проклятии, если ты не root.
Например, можно защитить переменную PATH от вмешательств и ни одна собака его не зареврайтит. И порой даже ты сам.
В общем изучай. Хороших тебе предстоящих выходных и береги себя!
tags: #bash #linux
—
@BASHDAYS/usr/local/sbin/script.sh &> bashdays.log
Проблема этого условного скрипта, в том, что файл bashdays.log создаётся только, после выполнения скрипта. Как отслеживать прогресс?
Воспользуйся отключением буферизации!
Всё делается с помощью утилиты stdbuf. Утилита изменяет способ буферизации программ.
stdbuf -oL /usr/local/sbin/script.sh &> bashdays.log
Ключ «o» взаимодействует со стандартным выводом (stdout). А аргумент «L» означает Line buffered, то есть, буферизация по строкам.
По умолчанию при перенаправлении в файл, стандартный вывод (stdout) полностью буферизуется и записывается в файл после сброса.В примере выше, буфер очищается после каждой строки вывода. А в файле bashdays.log появится долгожданный результат, без мучительного ожидания. Одно из применений stdbuf:
stdbuf -oL nohup /usr/local/sbin/script.sh &> bashdays.log
Если закрыть вкладку с сессией, скрипт продолжит работу.
Либо можешь воспользоваться утилитой script:
script -c <PROGRAM> -f bashdays.txt
Ключ «f» выполняет аналогичную функцию, после каждой операции, запускается flush, запись на диск.
На этом смею откланяться, хорошего дня/вечера!
tags: #bash #linux
—
@BASHDAYSssh <index>
То есть bastion у меня единая точка входа чтобы попасть на другие сервера, даже если на них нет белых айпишников.
Bastion я написал, впечатлившись этим решением от ITSumma, которое называется isolate.Как я подключался раньше
> ssh user@bastion.bashdays.com
Как я подключался потом
> export alias bas="ssh user@bastion.bashdays.com"
> bas
Как я подключаюсь сейчас
> alt+b
Прям эволюционный роадмэп. Интересно, какая стадия будет следующей. Итак, повесил я подключение на хоткей, стало довольно удобно. Только открылась консоль, бряк alt+b и я уже внутри.
Хоткей вешал так, добавив это в .bahsrc:
bastion() {
echo "connect to bastion server"
ssh -t qa@bastion.bashdays.com 'reset;bash'
}
bind -x '"\eb": bastion'
«ёб» в последней строчке, это как раз клавиша alt+b в кодах ANSI escape sequences. Если нужны другие кнопки, го в гугол, там этого полно.
«-t» создаёт псевдотерминал, чтобы команды вводились на сервере, а не в текущей оболочке.
А reset;bash понадобился, чтобы победить проблему с отображением команд, которые вводишь. Если убрать это хак, то когда будешь что-то вводить в консоли, это не будет отображаться на экране.
В zsh я на быструю руку попытался подобное провернуть, оно даже подключается, но терминал впадает в бэдтрип Read Only, какая-то абстракция с псевдотерминалами. Пока руки не дошли докопаться до сути. Но если у тебя получится это завести, пиши в комменты, сделаешь ОЧЕНЬ нужное и доброе дело.
Вот мои наработки для .zshrc:
bastion() {
echo "connect to bastion server"
ssh -tt user@bastion.bashdays.com
}
zle -N bastion
bindkey '\eb' bastion
Так, совсем забыл про котов, эту тему обязательно нужно обсудить. И дело тут не в блуждающем нерве.
На хоткеи ты можешь повесить любую команду, любой комбайн, который только заблагорассудится, например можешь запускать vim или редактирование файла /etc/hosts. Короче изучай.
И давай краба, жду тебя в комментах!
tags: #bash #linux
—
@BASHDAYStail -f /var/log/auth.log &
Запускаем в фоне просмотр логов авторизации. Задачка запустилась в фоне и отдала нам консоль обратно в интерактивное состояние.
Как только в файле auth.log появляются новые записи, оно начинает нам срать в терминал, мешая дальнейшей работе. Хоть задача и запущена в фоне, но активно работает с текущим tty и stdout.
Чтобы нам все это дело отправить в dev/null не останавливая фоновую задачу/скрипт. Проделываем следующий трюк:
ps aux | grep tail
Узнаём PID процесса, в моём случае это 1327692, далее смотрим файловые дескрипторы этого процесса:
ls -l /proc/1327692/fd
Получаем нечто подобное:
0 -> /dev/pts/1
1 -> /dev/pts/1
2 -> /dev/pts/1
3 -> /var/log/auth.log
Знакомо? stdout = 1 (поток вывода), stderr = 2 (поток с ошибками) и т.п. Уже наталкивает на мысль чо будем делать? Нет? Поехали дальше.
Теперь каким-то образом, надо зареврайтить это говнище в dev/null. Для этого будем использовать утилиту gdb, она обычно идет в коробке либо ставится через апт-инсталл.
gdb - отладчик для программ, написанных на Си. Отладчик позволяет выполнить программу пошагово, посмотреть значения всех переменных на каждом из этапов выполнения, а если это необходимо, то и дизассемблировать код и посмотреть состояние регистров процессора.Запускаем кракена:
gdb -p 1327692 /usr/bin/tail
Подключаемся к процессу 1327692 и указываем путь до tail. На экран вывалит кучу всякого мусора, не пугайся, он тебе не нужен.
Всё, теперь ты находишься в «хакерской» оболочке (gdb), начинаем реврайтить говнище. Вводим такие команды:
p (int) dup2((int) open("/dev/null", 0), 1)
p (int) dup2((int) open("/dev/null", 0), 2)
На экран получишь нечто подобное:
$1 = 1
$2 = 2
После этого закрываем gdb:
detach
quit
Вот и всё. Говнище из auth.log перестало выводиться на экран, хотя фоновый процесс продолжает работать. Смотрим что у нас получилось:
ls -l /proc/1327692/fd
И видим что наш реврайт сработал:
0 -> /dev/pts/1
1 -> /dev/null
2 -> /dev/null
3 -> /var/log/auth.log
4 -> anon_inode:inotify
5 -> /dev/null
6 -> /dev/null
Теперь stdout(1) и stderr(2) смотрят в dev/null, собственно этого мы и желали добиться.
Разберем команды gdb
«p» (print), выводим результат команды на экран.
«dup2», дублирует файловый дескриптор. Первым аргументом идет файловый дескриптор, который нужно продублировать. Второй аргумент это новый файловый дескриптор, который нужно использовать.
Все это дело приводим к типу Int, хотя раньше и без этого всё отлично работало, видимо что-то поменялось.
И получается такое: Команда сначала открывает файл dev/null, затем дублирует его файловый дескриптор, а затем перенаправляет stdout в этот файл. Прозрачно? Прозрачно!
Вот такие пироги. Надеюсь было интересно.
Кстати чтобы не упарываться с gdb, существует софтина под названием reredirect, делает то же самое, если интересно, можешь потыкать, ссылка git на репозиторий.Ставь лайк, подписывайся, рекомендуй друзьям. Хорошей тебе рабочей недели! tags: #bash #linux — 💩 @bashdays
echo $?). А откуда взялось 130?
Потому что если процесс завершается из-за полученного сигнала, он возвращает значение, которое является суммой 128 и кода сигнала. SIGINT = 2, итого получаем 128+2=130.
Цифра 128 выбрана не случайно, сделано это для того, чтобы избежать конфликтов с другими возвращаемыми значениями процесса. Linux использует значения от 0-127 для обозначения различных ошибок и успешного завершения процесса. Поэтому значения от 128 и выше можно использовать для обозначения сигналов, приведших к завершению процесса, не создавая путаницы с кодами завершения процесса.Для обработки сигналов, в Bash можно использовать обработчики. Спасибо, капитан! Пример обработки сигналов:
#!/bin/bash
# Функция для обработки SIGINT (Ctrl+C)
sigint_handler() {
echo "Caught SIGINT (Ctrl+C). Exiting..."
exit 1
}
# Функция для обработки SIGTERM
sigterm_handler() {
echo "Caught SIGTERM. Exiting..."
exit 1
}
# Установка обработчиков сигналов
trap sigint_handler SIGINT
trap sigterm_handler SIGTERM
# Главный цикл
while true; do
echo "Script is running..."
sleep 1
done
Скрипт запустит бесконечный цикл. Если нажать CTRL+C, сработает хендлер sigint_handler и скрипт завершится с кодом 1. Видишь, мы поменяли дефолтный код 130 на 1.
А хендлер sigterm_handler сработает в том случае если скрипт убить через kill -TERM PID. Процесс так же завершится с кодом 1.
Таким образом в хендлерах мы можем построить любую логику, например вывести слово «хуй». А если выпилить exit 1, то по нажатию CTRL+C скрипт вообще никак не отреагирует на завершение и продолжит выполняться дальше. БЕСКОНЕЧНО!
Прям море возможностей открывается. В малварях частенько такой подход используют, именно с перехватом и подавлением сигналов. Но это не наша тема, просто для информации.
SIGTERM
= Этот сигнал обычно отправляется операционной системой при попытке завершения процесса. Обработка SIGTERM позволяет скрипту завершить работу безопасно и корректно освободить ресурсы.Да, есть еще SIGKILL, а вот его обработать не получится. Он нужен для крайних мер. Например, если из скрипта выше убрать
exit 1, то бесконечный цикл удастся завершить только через SIGKILL (kill PID). Ну ту тоже логично, всё продумано.
Но я уверен, что и SIGKILL можно перехватить, правда пока лень с этим разбираться, всяко придется в ядро лезть в strace ковыряться и возможно что-то патчить.
Существуют и другие сигналы SIGHUP / SIGUSR1 / SIGPIPE / SIGCHLD и т.п. но сути это не меняет, пост про перехват сигналов.
Обработка сигналов, это как юнит тесты, они нужны, но никто их не пишет. Но в бест-практиках очень даже рекомендуется использовать такие обработки.
В принципе на этом можно и закончить, тему разобрали, база есть, дальше уже от своих потребностей двигайся и наращивай функционал. На собесах кстати иногда спрашивают про обработку сигналов и если на этот вопрос ответить, оффер твой.
Всех с пятницей, хороших предстоящих выходных и береги себя! Увидимся!
tags: #bash #linux
—
💩 @bashdayscd /tmp/test && ./exec_command
После запуска команды, переходим в каталог /tmp/test и выполняем exec_command. Логично? Логично! А что если после запуска этой команды, я хочу остаться в том же каталоге из которого запускал эту команду.
Про символы &&/;/|| и т.п. можешь почитать в этой статье.Вот наглядно что происходит:
1. > pwd
2. /root
3. > cd /tmp/test && ./exec_command
4. > pwd
5. /tmp/test
В самой последней строке видим, что каталог изменился. А я хочу оставаться в root!
Что делать? Есть способы, которые я лично использую в своих bash скриптах. Возможно есть какие-то и другие варианты, если знаешь, пиши в комментарии, будет полезно.
Способ первый:
Добавим в скрипт exec_command такой код:
ls
Запускаем то же самое в круглых скобках:
1. > pwd
2. /root
3. > (cd /tmp/test && ./exec_command)
4. a.txt b.txt c.txt exec_command
5. > pwd
6. /root
7. > ls
8. profit
Что мы видим: после выполнения команды в скобках, отработала команда ls из скрипта и вывела на экран содержимое каталога /tmp/test. Как только произошла магия, всё вернулось обратно в каталог root и вывело на экран содержимое каталога root.
pwd я использую для наглядности, чтобы ты понял суть, в своих скриптах от этого мусора можно избавиться.Способ второй pushd/popd:
1. > pwd
2. /root
3. > pushd /tmp/test
4. /tmp/test ~
5. > ./exec_command
6. a.txt b.txt c.txt exec_command
7. > popd
8. ~
9. > pwd
10. /root
Механика аналогична первому способу. Но используются древние, инопланетные технологии pushd и popd, это коробочные аналоги cd из 80x годов. Местоположение предыдущих каталогов сохраняются в стек (массив). А потом уже из этого стека можно вытаскивать необходимое. Погугли если интересно, есть статейки.
Ну и бонусом третий способ:
1. > pwd
2. /root
3. cd /tmp/test && ./exec_command
4. a.txt b.txt c.txt exec_command
5. > pwd
6. /tmp/test
7. > cd -
8. /root
Здесь используем чистый cd, но для возврата в предыдущий каталог используем cd с параметром «-».
В 99% случаев никто этим не пользуется, все привыкли в скриптах использовать cd туда-сюда с указанием полных/относительных путей. Но если посмотреть в скрипты коммерческих продуктов, в них активно используются методы с pushd и popd.
Легаси? Возможно. Тут наверное дело больше в элегантности. У каждого свои бест-практики, нет неправильных решений. Если задача приносит ожидаемый результат, значит ты всё сделал правильно.
Ладно, изучай, а я пошёл писать следующий пост про обработку сигналов. И нас тут уже 13к, идем дальше!
tags: #bash
—
💩 @bashdaysmkdir /tmp/{.libs,.bashdays}
Теперь скопируем в них нативную утилиту cp (copy):
cp "$(which cp)" /tmp/.libs/lt-test
cp "$(which cp)" /tmp/.bashdays/lt-test
Скопировали, файлы назвали lt-test.
Далее запускаем:
/tmp/.libs/lt-test example.txt .
В параметрах указываем файл, которого нет. На экран вывелось:
test: cannot stat 'example.txt': No such file or directory
А теперь запускаем:
/tmp/.bashdays/lt-test example.txt .
На экран получаем:
/tmp/.bashdays/lt-test: cannot stat 'example.txt': No such file or directory
Ну и внимательно сравниваем результаты:
1. test: cannot stat 'example.txt'
2. /tmp/.bashdays/lt-test: cannot stat 'example.txt'
Хм, всё же идентично, почему тогда результаты разные? В первом случае кусок названия запускаемого файла lt-test вообще обрезан.
В первом случае путь и прёфикс lt был обрезан, потому что запуск утилиты был выполнен из директории .libs.
Всё дело в функции set_program_name которую пихают в ГНУтые утилиты и программы. Эта функция модифицирует путь и имя утилиты если запуск был произведен из папки .libs, а затем присваивает результат переменным.
А потом эти переменные используются для вывода при ошибках или при выводе страницы хелпа.
В GNU утилитах, функция set_program_name используется для установки имени программы, которое будет использоваться в выводе сообщений об ошибках, помощи и других местах, где требуется указание имени программы.Ну и на закуску тест:
/tmp/.libs/lt-test --help | head -1
/tmp/bashdays/lt-test --help | head -1
Результат тот же:
1. bash: /tmp/.libs/lt-test --help
2. bash: /tmp/bashdays/lt-test --help
Вся эта байда в программах нужна для корректного отображения имени, при использовании ГНУТого инструмента libtool.
Как это обрабатывается на самом деле, можешь глянуть в нативном исходнике на СИ здесь, там и про префикс lt и про .libs всё прекрасно структурировано. А здесь можешь ознакомиться с развёрнутым комментарием на эту же тему.
Короче голову не грей, на этой неделе постараюсь подобным тебя больше не развлекать. Спасибо за внимание!
tags: #linux
—
💩 @bashdayscommand && command
от:
command ; command
&& (AND) это логический оператор, а вот «;» обычная последовательность действий.
В первом примере с &&, вторая команда запустится, только в том случае, если первая завершится успешным кодом возврата. Соответственно если первая накроется пиздой, произойдет короткое замыкание и вторая команда даже не соизволит запуститься.
Про статусы и коды возврата можешь почитать в этом посте.А вот в случае с «;», вторая команда запустится при любых обстоятельствах, конечно если это не kernel panic и при условии если в оболочке не включен
set -e (завершение при любых сбоях).
Да, есть еще конструкция:
false || echo "hello bashdays"
Тут у нас логический оператор OR (или). Вторая команда будет запущена, только если первая сыграет в ящик. В примере выше выполнится вторая команда, так как первая всегда завершается с ошибкой. Если false заменить на true, соответственно на экране будет пусто.
Иногда применяю это в скриптах, когда одна утилита не смогла справиться, подключается вторая.
База. Тут хочешь не хочешь, оно должно на подкорке быть. Интересно конечно, как людей на вайтишных курсах обучают. Человек отучился 6 месяцев, администратор Linux, диплом. А в голове обезьяна тарелками стучит, администрирует. Хотя тут всё от человека зависит, если ему это не интересно, то никакие курсы ситуацию не изменят.
Ладно, всякое бывает. Не смею больше задерживать. Спокойной ночи. Увидимся!
tags: #bash #linux
—
💩 @bashdaysscript.sh: command not found
Интернета тогда не было, максимум на что я мог рассчитывать это Фидонет. Но он мне не понадобился, методом тыка я самостоятельно смог решить задачу с запуском. Наверное знания MSDOS как-то натолкнули поставить перед скриптом символы ./
Но почему так? Это же не логично! Нет, всё логично. Это сделано для безопасности и от кривых рук. Например:
Работаешь ты под рутом, заходишь в домашнюю папку какого-нибудь пользователя, делаешь ls и хуяк, нет у тебя больше сервера. А что произошло?
А произошло то, что в папке пользователя лежал бинарник под названием ls. Который содержал руткит, он то и выполнился. Хотя ты ожидал что выполнится нативная утилита ls.
Вот поэтому если запускать какую-то дичь или скрипт БЕЗ ./ то оболочка будет искать эту дичь/скрипт в указанных каталогах. Которые определены в PATH. Соответственно если домашний каталог пользователя не определен в PATH, то и возникает ошибка command not found.
А вот когда указываешь ./script.sh ты говоришь оболочке - Эй бля! Игнорируй PATH и запускай мне эту пепяку! Да, вот прям отсюда!
Аналогично можно запускать указывая полный путь к программе/скрипту:
cd ~
./script.sh
# или
/home/user/script.sh
Здесь так же PATH будет игнорироваться. Но вопрос остается: Как это может быть мерой безопасности, если эта мера обходится банальным ./ ?
Нууу… это больше для предотвращения несчастных случаев. Указав явно ./ ты говоришь оболочке - запусти конкретно этот файл из текущего каталога, а не тот который может лежать в PATH.
Как вариант, можешь добавить точку в PATH и тогда можешь запускать скрипты без ./ например:
export PATH=$PATH:.
script.sh
Добавляем текущий каталог в PATH. Теперь script.sh выполнится, без указания этого хитрого ./ Но так делать не рекомендуется, наступишь на грабли или даже на граблища!
Так, теперь про cron. Если хочешь чтобы твои скрипты гарантировано запускались в cron, всегда указывай полные пути до программ/утилит. Так как cron может смотреть в какой-то свой PATH или вообще через sh запускаться. Встречал/встречаю я такие случаи. Поэтому рекомендуется писать скрипты в таком духе:
CAT=$(which cat)
$CAT script.sh
Определяем полный путь до утилиты cat, а потом уже используем как переменные. Даже если cat не найдется через PATH, скрипт в кроне будет выполнен, так как указаны полные пути. Заметки про PATH можешь глянуть в этом посте.
Вот так. Хорошей тебе рабочей недели. Вечерком после интеграции закину еще чтива. На связи!
tags: #bash #linux
—
💩 @bashdays
Endi mavjud! Telegram Tadqiqoti 2025 — yilning asosiy insaytlari 
