Bash Days | Linux | DevOps
Авторский блог от действующего девопса Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу. Автор: Роман Шубин Реклама: @maxgrue MAX: https://max.ru/bashdays Курс: @tormozilla_bot Блог: https://bashdays.ru
Mostrar más📈 Análisis del canal de Telegram Bash Days | Linux | DevOps
El canal Bash Days | Linux | DevOps (@bashdays) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 23 625 suscriptores, ocupando la posición 5 600 en la categoría Tecnologías y Aplicaciones y el puesto 27 629 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 23 625 suscriptores.
Según los últimos datos del 08 noviembre, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -7, y en las últimas 24 horas de -2, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 17.47%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 10.84% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 4 126 visualizaciones. En el primer día suele acumular 2 560 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 14.
- Intereses temáticos: El contenido se centra en temas clave como bashdays, linux, bash, docker, скрипт.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Авторский блог от действующего девопса
Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.
Автор: Роман Шубин
Реклама: @maxgrue
MAX: https://max.ru/bashdays
Курс: @tormozilla_bot
Блог: https://bashdays.r...”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 09 noviembre, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
echo -e "\x1b[39;42mBashdays"Но как мы знаем, всё уже придумано за нас, так получилось и в моем случае. Порыскав на просторах вселенной, я нашел несколько сервисов, которые позволяли мышкой натыкать нужную конфигурацию цветов и получить готовый ANSI код. Самым топовым сервисом оказалась поделка ansicodes никому не известного разработчика. Собственно так и бывает, великие умы - славы не ищут. 💩 Сервис доступен по этой ссылке 🐱 Страница проекта на github Натыкиваешь мышкой нужную тебе конфигурацию цветов для консоли, по итогу получаешь готовый ANSI код, который вставляешь в свои скрипты и получаешь новогоднюю елку. Четко, быстро и без депрессий. Ну и можно на свой сервак воткнуть, там ничо сложного нет, обычный html + js. Есть альтернатива 🐱 ansi, выполнена в роле отдельной утилиты, которую можно воткнуть в систему. Проект известный, но опять же в узких кругах. Но тут есть зависимости, а я не люблю зависимости, предпочитаю использовать коробочные инструменты. Ну и всегда помни - если собираешься добавлять цвета в свой скрипт, всегда добавляй опцию —no-color, чтобы люди, анализирующие выходные данные скрипта, не испытывали к тебе ненависти. Вот такие пироги! tags: #linux #bash #utilites — 🟢 Подпишись: @bashdays
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru, erid: 2VtzqvyHF39whiptail --yesno "читаешь @bashdays?" 10 50Откроется диалоговое окно с двумя кнопками yes/no, с текстом + высотой и длиной. Помимо инпута yes/no имеется ряд других опций: msgbox - информативное окно с кнопкой OK infobox - херня какая-то, мигает и закрывается inputbox - поле для ввода текста passwordbox - поле для ввода пароля, скрыт звездочками textbox - выводит содержимое файла в окне menu - реализация меню checklist - чекбоксы radiolist - радиобатоны gauge - прогрессбар В общем подойдет на все случаи жизни. Что интересно, можно реализовать неплохое меню, либо красиво отобразить на экране результат работы утилиты. Полет фантазии безграничный. Теперь про то, как все это замешать в bash скрипты. Логика простая. Если пользователь выбирает Yes, то whiptail вернет 1. Если No, то вернет 0. Если возникла ошибка, либо принудительное завершение, будет возвращен код -1. Например:
if (whiptail --yesno "Choose Yes or No" 10 60) then
echo "You chose Yes. Exit status was $?"
else
echo "You chose No. Exit status was $?"
fi
Дефолные кнопки Yes/No можно зареврайтить с помощью опций --yes-button и --no-button.
whiptail --yes-button "Fuck" --no-button "Yeee" --yesno "What?" 10 60Теперь вместо Yes/No наблюдаем Fuck/Yeee. Ну а как получать данные, которые ввели руками? А вот так:
NAME=$(whiptail --inputbox "What is your name?" 10 60 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]; then
echo "Your name is:" $NAME
else
echo "You chose Cancel"
fi
На экран выведется, то что, ты введешь в инпуте. Таким образом можно дальше строить диалоговые боксы и гонять лысого между ними введенные и выбранные переменные.
Реализация меню:
OPTION=$(whiptail --menu "Choose" 15 60 2 "1" "Linux" "2" "Windows" 3>&1 1>&2 2>&3)
exitstatus=$?
if [ $exitstatus = 0 ]; then
echo "Your chosen:" $OPTION
else
echo "You chose Cancel."
fi
В остальных случаях всё делается идентично. Но что такое 3>&1 1>&2 2>&3? Это магия и скучная теория.
Перенаправление ввода-вывода stdin/stdout/stderr. Если коротко, без этого ничего не заработает. Скучная теория. В будущем расскажу про все эти перенаправления stdin/stdout/stderr на котиках. Пока просто делай как написано.
На закуску лови реализацию прогрессбара, тут хоть что-то происходит и движется:
{
for ((i = 0 ; i <= 100 ; i+=20)); do
sleep 1
echo $i
done
} | whiptail --gauge "Loading..." 6 60 0
Основное рассмотрели. Можно брать и что-то уже клепать вменяемое. У whiptail есть аналог, называется dialog, посмотри, возможно он подойдет именно тебе, синтаксис похожий.
💩 Картиночки собрал тут, чтобы визуально оценить масштабы всей этой суеты.
Если есть вопросы и предложения, задавай в комментариях. Давай, увидимся вечером, хорошего тебе дня!
tags: #linux #bash #utilites
—
🟢 Подпишись: @bashdaysstrace -Yyf mktempY = выводим команды с привязкой к PIDs y = выводим пути привязанные к дескрипторам файла f = мониторим форки процессов Можно и без ключей запускать, я просто так привык. После запуска получаем длинную портянку. Мне интересна лишь концовка, а именно:
openat(AT_FDCWD</root>, "/tmp/tmp.V0051oIfmC", O_RDWR|O_CREAT|O_EXCL, 0600) = 3</tmp/tmp.V0051oIfmC>Ага, отловили openat. Идем сюда читать список ошибок которые возвращает openat. Меня интересует:
EEXIST pathname already exists and O_CREAT and O_EXCL were used.Если были заданы флаги O_CREAT и O_EXCL и файл существует, то возвращается ошибка EEXIST. Дополнительно нужно выяснить с какого по счету системного вызова будем перехватывать и возвращать ошибку. Суть в том, что при запуске mktemp есть несколько вызовов openat. Их делает динамический загрузчик. Если их перехватить, то программа просто не запустится. Также нужно избавиться от нескольких вызовов openat которые совершает система языковых переводов. Для этого нужно установить значения переменной LC_ALL в POSIX. Выводим все openat которые делает утилита:
LC_ALL=POSIX strace -Yyfe openat mktempУ меня получилось 4 строки:
openat(AT_FDCWD</root>, "/etc/ld.so.cache") openat(AT_FDCWD</root>, "/lib/x86_64-linux) openat(AT_FDCWD</root>, "/dev/urandom")\ openat(AT_FDCWD</root>, "/tmp/tmp.eyg53MQ1c2")Вызов создающий временный файл у меня по счету 4й. Выпускаем кракена:
LC_ALL=POSIX strace -YCyfe inject=openat:error=EEXIST:when=4+ mktempВот это поворот! Ждем какое-то время и наблюдаем, как на экране бежит какая-то дичь. Это твои личные файлы зашифровываются. Шутки за 300. Короче дождались пока оно там просрется и выведет табличку с результатами. Смотрим в колонку errors для системного вызова в openat и видим число 238328. Вот собственно столько раз утилита mktemp будет пытаться создать файл с новым именем, после того как системный вызов будет возвращать ошибку, означающую что файл существует. Получается количество попыток 62**3 = 238328. Упрямая утилита! Эксперимент завершен, результат получен, мы молодцы! На этом собственно все, немного сложновато, но очень интересно. Потыкай сам и всё станет еще более прозрачным. Давай пять, увидимся! tags: #linux #debug #utilites — 🟢 Подпишись: @bashdays
strace -Yyf mktempY = выводим команды с привязкой к PIDs y = выводим пути привязанные к дескрипторам файла f = мониторим форки процессов Можно и без ключей запускать, я просто так привык. После запуска получаем длинную портянку. Мне интересна лишь концовка, а именно:
openat(AT_FDCWD</root>, "/tmp/tmp.V0051oIfmC", O_RDWR|O_CREAT|O_EXCL, 0600) = 3</tmp/tmp.V0051oIfmC>Ага, отловили openat. Идем сюда читать список ошибок которые возвращает openat. Меня интересует:
EEXIST pathname already exists and O_CREAT and O_EXCL were used.Если были заданы флаги O_CREAT и O_EXCL и файл существует, то возвращается ошибка EEXIST. Дополнительно нужно выяснить с какого по счету системного вызова будем перехватывать и возвращать ошибку. Суть в том, что при запуске mktemp есть несколько вызовов openat. Их делает динамический загрузчик. Если их перехватить, то программа просто не запустится. Также нужно избавиться от нескольких вызовов openat которые совершает система языковых переводов. Для этого нужно установить значения переменной LC_ALL в POSIX. Выводим все openat которые делает утилита:
LC_ALL=POSIX strace -Yyfe openat mktempУ меня получилось 4 строки:
openat(AT_FDCWD</root>, "/etc/ld.so.cache") openat(AT_FDCWD</root>, "/lib/x86_64-linux) openat(AT_FDCWD</root>, "/dev/urandom")\ openat(AT_FDCWD</root>, "/tmp/tmp.eyg53MQ1c2")Вызов создающий временный файл у меня по счету 4й. Выпускаем кракена:
LC_ALL=POSIX strace -YCyfe inject=openat:error=EEXIST:when=4+ mktempВот это поворот! Ждем какое-то время и наблюдаем, как на экране бежит какая-то дичь. Это твои личные файлы зашифровываются. Шутки за 300. Короче дождались пока оно там просрется и выведет табличку с результатами. Смотрим в колонку errors для системного вызова в openat и видим число 238328. Вот собственно столько раз утилита mktemp будет пытаться создать файл с новым именем, после того как системный вызов будет возвращать ошибку, означающую что файл существует. Получается количество попыток 62**3 = 238328. Упрямая утилита! Эксперимент завершен, результат получен, мы молодцы! На этом собственно все, немного сложновато, но очень интересно. Потыкай сам и всё станет еще более прозрачным. Давай пять, увидимся! tags: #linux #debug #utilites — 🟢 Подпишись: @bashdays
apt/yum install shfmt. Ну и запускается так же элементарно:
shfmt script.shСкармливаем утилите свой прекрасный скрипт, а на экран выводится приятный глазу отформатированный результат. Ну либо не выводится, если встречаются какие-то логические ошибки. Короче утилита выкашивает весь ненужный трешак, но сохраняет «наверное» работоспособность скрипта. Чтобы каждый раз не копипастить результат форматирования, у shfmt есть ключ -w, указав его, результат сразу перезапишется в файл над которым проводятся эксперименты. Какого-то жесткого кодстайла нет, всё настраивается через ключи, например можно указать количество пробелов и т.п. Закидываем в алиасы и пользуемся. Я обычно использую shfmt для подготовки скриптов для публикации в публичные репозитории github. Чтобы оупенсорц гуру гавном не закидывали на этапе плохого форматирования. По секрету скажу, что в личных проектах (да и не только) особо никто не придерживается какому-то определенному форматированию. Все пишут как
head -c 1G /dev/urandom > /tmp/largeЕсли head не понимает буковку G в размере, то указываем явный размер файла: -c
1073741824.
Так, файл размером 1G готов /tmp/large, дальше запускаем:
strace -P /tmp/large -ye read grep -q 's' /tmp/largeКлючи: -P = путь до файла -y = выводит имя пути до файла -e = команда которую дебажим Через секунду, на выходе получаем нечто подобное:
read(3</tmp/large>, "v\310*A\307\16\324m&V8H\202\326\177\244\3059\27}00_\274\300<\245.X\27\310`"..., 98304) = 98304 +++ exited with 0 +++То есть с опцией -q, grep не стал читать полностью весь гигабайтный файл, а сделал лишь один системный вызов read(3</tmp/large) и тут же завершил работу. Искомая строка нашлась, вернулся статус 0. Про статусы выхода я писал в этом посте. Делаем то же самое без strace и проверяем статус выхода, все ли идентично:
grep -q 's' /tmp/large echo $?На выходе у нас будет 0. Ок, эксперимент завершился успехом. Некоторые версии grep могут возвращать истину если ошибка произошла не связанная с поиском. Этот момент нужно учитывать. Про такие случаи хорошо написано здесь. Если коротко, некоторые версии grep с ключом -q могут вернуть статус выхода 2. Так что на разных системах работать может по-разному. Если в своих скриптах проверяешь эти статусы, логика может сломаться, будь аккуратнее с этим. Еще нюанс, можно добиться такого же поведения grep, но без ключа -q. Это сработает, если вывод перенаправлен в /dev/null. Ну это и логично, нам не нужен никакой вывод на экран и нет никакого смысла продолжать читать огромный файл если подстрока уже найдена.
strace -P /tmp/large -ye read grep 's' /tmp/large > /dev/nullУтилита grep (без ключа -q) так же сделает один системный вызов read и прекратит свою работу, как только найдет первое совпадение. Вроде не сложно рассказал, по крайней мере попытался. Ладно, рад всех видеть, побежал дальше работу работать, сумасшедший какой-то день. Пока пока! tags: #linux #bash #debug — 🟢 Подпишись: @bashdays
#!/bin/bash echo "Fucking" exit 1Что делать? Ну во первых прописываем прицельную двоечку в безмозглую кабину Пете. Ну а если ты на удаленке, то просто пишем в корпоративную аську — Пётр, вы 3.14дарасина. Во вторых запускаем команду:
grep -a -C 200 -F 'Fucking' /dev/sda1Оно там подумает какое-то время и начнет вываливать на экран всякий мусор. Ну а среди этого мусора будет удалённый исходник скрипта. Копипастим/Вставляем и снова радуемся своим скиллам. Теперь по ключам: -a = осуществляем поиск в бинарных файлах. -C = сколько строк выводить ДО и ПОСЛЕ каждого совпадения строки. То есть у меня совпала строка Fucking, если я не укажу -С 200, то увижу в выводе только echo "Fucking". А нам то нужно восстановить весь скрипт полностью. Поэтому и нужно сказать грепу, давай еще выведи 200 строк до и 200 после. Кстати с ключом -C удобно грепать логи какого нибудь php, где нужно посмотреть контекст ошибки, обычно она на несколько строк простилается. -F = строка, по которой будем искать. Естественно указываем свой раздел диска, у меня это /dev/sda1, у тебя может быть что-то совсем другое. Учти этот момент. Почему это работает? Если коротко - мы воспользовались философией Unix, которая гласит — Всё есть файл! Такие дела. Чётко и полезно, забирай в копилку знаний. Вечерком еще что-нибудь интересное напишу, пока правда не знаю про что. На связи! tags: #linux #recovery — 🟢 Подпишись: @bashdays
echo '!0LPQsNCy0L3Qvg==' | base64 -d base64: invalid inputА как она понимает какой корректный символ, а какой нет? А тот который входит в алфавит кодирования. Символ новой строки, также входит в этот алфавит и просто игнорируется утилитой, где бы этот символ не находился. Алфавит кодирования содержит латинские символы A-Z, a-z, цифры 0-9 (всего 62 знака) и 2 дополнительных символа. В приведенном выше примере, символ «!» не входит в этот алфавит. Чтобы декодировать данные, можно удалить символ восклицательного знака. Но мы с вами калачи тертые и любим всё необычное. Поэтому просто указываем ключ -i и все символы, которые не входят в алфавит кодирования, просто-напросто будет проигнорирован. Заебись! Хорошо!
echo '!0LPQsNCy0L3Qvg==' | base64 -diОпа! И нам тут даже восклицательный знак не помеха, закодированный текст успешно вывелся. Вставляя «плохие» символы в закодированную строку, мы можем как бы запретить декодирование. Этакий анти скрипт-кидди получается. Наивный замочек. Вставлять «плохие» символы можно в любую часть строки. При раскодировании с ключом -i все эти «плохие» символы будут проигнорированы, где бы они ни были и замочек откроется. Типа того (вставил ! в середину строки):
echo '0LPQsNC!y0L3Qvg==' | base64 -diЗакодированная срока успешно раскодируется. А если пойти дальше, можно в обычном тексте передавать закодированные данные:
echo 'Съешь ещё этих мягких французских булок и разжирей 0YXRg9C5' | base64 -diТекст в игнор, а данные раскодируются. Таким образом можно защищать передаваемые данные между фронтом и бэкендом по API. Отловил кто-то POST запрос, выудил из него закодированную строку base64, а расшифровать не получается. Если тонкостей не знать, на этом все и закончится. Никто особо не будет ковыряться в огромном закодированном тексте и искать, что не так. 100 лет назад были малвари которые управлялись из твиттера. Малваря заражала машину, потом лезла в твиттер, искала по-индивидуальному хэштегу сообщение, находило его и выполняла инструкции на зараженной машине. Сообщение выглядело безобидно, но в конце содержались закодированные команды, с помощью которых и управлялась малваря. Такой способ избавлял злоумышленников от необходимости завязываться на контрольный центр, который в любой момент могли заблокировать по домену или айпишке. Эхх… были времена. Ладно, надеюсь было познавательно, всем добра! tags: #linux #utilites — 🟢 Подпишись: @bashdays
echo "test" >> /tmp/test.txt и потом с ним продолжить работу прям из скрипта cat /tmp/test.txt. То есть редакторы позволяют работать с файловой системой и не ограничены лишь компиляцией.
В общем тыкайте кому интересно. Конечно использование локального bash будет более правильным решением, но в каких-то случаях выручают именно онлайн сервисы, тем более они бесплатные.
После интеграции от партнера будет еще один пост на техническую тему, так что далеко не отключайтесь. Да, всем привет! Спасибо ребят, кто подбустил и дал возможность выкладывать сторисы.
tags: #services #bash
—
💩 @bashdaystouch /tmp/script.sh и закинем в него такое:
#!/bin/bash sleep 1000 exitДелаем исполняемым
chmod +x /tmp/script.sh и запускаем в фоне /tmp/script.sh &
Символ & может служить разделителем между командами command & command, две команды выполнятся параллельно.
Так, скрипт запустили, он крутится у нас фоне, давай теперь удалим сам файл:
rm -f /tmp/script.sh ключ -f = force, удалит без лишних вопросов.
Окей. Файл удалили. Как восстановить? Выполняем:
lsof -c 'script.sh'На экран выкатится портянка, нам нужна строка где в конце указан путь до скрипта, который был удален:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME script.sh 261899 root 255r REG 8,1 51 130773 /tmp/script.shБерем PID = 261899, берем FD = 255 и делаем сальто-мортале:
cat /proc/261899/fd/255Опачки, что мы видим? Исходник скрипта, который мы удалили:
File: /proc/261899/fd/255 #!/bin/bash sleep 1000 exitКопипастим, вставляем, сохраняем и гордимся своими охренительными скиллами. Почему это возможно? А почему бы и нет! В следующих постах расскажу про общую концепцию удаления файлов в Linux и все станет прозрачным. Про восстановление файлов на диске, я как-то ранее уже писал тут, но там была немного другая история (когда мы знаем physical_offset). ☑️ читать:
man 5 proc # /proc/[pid]/fd/ man lsofВсем хороших предстоящих выходных и берегите себя! tags: #linux #bash — 🟢 Подпишись: @bashdays
Please analyze these repository and detect a common theme (e.g. programming language, technology, domain). Pay attention to language too (english, chinese, korean, etc.). If there is no common theme found, please say so. Otherwise, If you can find a strong signal for a common theme please come up with a specific name for imaginary country that contains all these repositories. Give a few options. When you give an option prefer more specific over generic option (for example if repositories are about recommender systems, use that, instead of generic DeepLearning)Хм, буду теперь названия переменных придумывать таким способом, пусть проклятые роботы за меня пашут. И даже поиск работает, чудо 🍴 Можно натолкнуться на достаточно интересные штуки, которые известны лишь узкому числу лиц и особо нигде не пиарятся. Потыкать карту можешь: 🐱 тут. Страница проекта с подробностями: 🐱 тут. tags: #services #git — 🟢 Подпишись: @bashdays
user ${nginx_user};
worker_processes auto;
pid ${pid_path};
include /etc/nginx/modules/*.conf
Всё то, что нужно отрендерить, располагаем в ${параметр}.
Создаем скрипт с логикой:
>> nginx_gen.sh && chmod +x nginx_gen.shСимволы «>>» означают - создать новый файл. А чмодиком делаем файл покорным и исполняемым. Еще есть фича с созданием файла через
touch nginx_gen.sh. Если запустить команду с touch повторно, то файл не перетрется, НО у него обновится дата и время создания. Иногда бывает полезно обновлять дату и время каким-нибудь файлам, которые выполняют роль флагов.
Так, поехали рендерить:
#!/bin/bash
function render {
export nginx_user=$1 pid_path=$2
cat nginx.tpl | envsubst > /etc/nginx/nginx.conf
}
if [[ $(hostname) == "production" ]]; then
nginx_user="www-data"
pid_path="/run/nginx.pid"
render $nginx_user $pid_path
else
nginx_user="nginx"
pid_path="/var/run/nginx.pid"
render $nginx_user $pid_path
fi
Разбираем портянку. Логика простая, если hostname равен production, то присваиваем одни переменные. Во всех других случаях присваиваем другие переменные.
Далее вызываем функцию render и передаем в нее nginx_user и pid_path. Функция render всё это дело экспортирует в переменные окружения, а затем с помощью envsubst заменяет их в шаблоне. Готовый конфиг сразу попадает в папку с nginx. Охуенно! Кайф!
envsubst - заменяет переменную окружения новым значением в формате строки оболочки командной строки. Переменные могут быть заменены в формате ${var} или $var
Если у тебя много переменных, то можешь их разом экспортировать через цикл. В предыдущих постах у меня где-то есть примеры таких циклов.
Собственно на этом можно и заканчивать. В 99% случаев все используют какой-то вонючий perl, sed, eval и т.п. Но решение с envsubst намного гибче и элегантнее.
Есть еще вариант с Heredoc. Heredoc-синтаксис — способ определения строковых переменных в исходном коде программ.
Пример скрипта, делает то же самое. Логику добавлять не стал:
#!/bin/bash
nginx_user="www-data"
pid_path="/var/run/nginx.pid"
cat > /etc/nginx/nginx.conf << EOF
user ${nginx_user};
worker_processes auto;
pid ${pid_path};
include /etc/nginx/modules-enabled/*.conf;
EOF
Отрендерится как нужно, но мне все же ближе вариант с envsubst. Нагляднее чтоли. Выбор лишь за тобой, на каком велосипеде кататься. Вот такие пироги. Ладно, побежал я дальше работу работать. Будьте здоровы! Увидимся!
tags: #linux #bash
—
🟢 Подпишись: @bashdays