Bash Days | Linux | DevOps
Авторский блог от действующего девопса Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу. Автор: Роман Шубин Реклама: @maxgrue MAX: https://max.ru/bashdays Курс: @tormozilla_bot Блог: https://bashdays.ru
Больше📈 Аналитический обзор Telegram-канала Bash Days | Linux | DevOps
Канал Bash Days | Linux | DevOps (@bashdays) языкового сегмента Русский является активным участником. Сейчас сообщество объединяет 23 788 подписчиков, занимая 5 702 место в категории Технологии и приложения и 28 099 место в регионе Россия.
📊 Показатели аудитории и динамика
С момента создания невідомо проект демонстрирует стремительный рост, собрав аудиторию из 23 788 подписчиков.
Согласно последним данным от 19 июня, 2026, канал показывает стабильную активность. За последние 30 дней изменение числа участников составило -226, а за последние 24 часа — 1, при этом общий охват остаётся высоким.
- Статус верификации: Не верифицирован
- Уровень вовлечённости (ER): Средний показатель вовлечённости аудитории составляет 23.40%. В первые 24 часа после публикации контент обычно набирает 13.11% реакций от общего числа подписчиков.
- Охват публикаций: В среднем каждый пост получает 5 567 просмотров. В течение первых суток публикация набирает 3 119 просмотров.
- Реакции и взаимодействия: Аудитория активно поддерживает контент: среднее количество реакций на один пост — 22.
- Тематические интересы: Контент сосредоточен на ключевых темах, таких как bashdays, linux, bash, docker, скрипт.
📝 Описание и контентная политика
Автор описывает ресурс как площадку для выражения субъективного мнения:
“Авторский блог от действующего девопса
Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу.
Автор: Роман Шубин
Реклама: @maxgrue
MAX: https://max.ru/bashdays
Курс: @tormozilla_bot
Блог: https://bashdays.r...”
Благодаря высокой частоте обновлений (последние данные получены 20 июня, 2026) канал поддерживает актуальность и высокий уровень охвата публикаций. Аналитика показывает, что аудитория активно взаимодействует с контентом, что делает его важной точкой влияния в категории Технологии и приложения.
bashErr=$(find src/ -type f -name '*.sh' -exec bash -n {} \; 2>&1 > /dev/null)
if [ -n "$bashErr" ]; then
echo "$bashErr"
exit 1
fi
После запуска этого хука проверяются все * .sh скрипты в папке src. Пример скрипта с ошибкой:
echo "Hello Bashdays
exit 0
src/script.sh: 7: Syntax error: Unterminated quoted string и запретит (коммитить, пушить в мастер).
Нечто подобное было прикручено к puppet очень давно в одной студии, был какой-то внутренний линтер на хуке гита, но хлебнул я знатно с ним. Мне пушить, а он на какие-то фантомные пробелы орет падла. Короче как только встал у руля, избавился и от этого вонючего паппета, от хуков и от команды.
Ну или как вариант можно включать set -u, например если переменную не объявил и попытался где-то её использовать.
set -u включает режим обработки неустановленных переменных (также известный как режим nounset). Когда этот режим включен, обращение к неустановленной переменной приведет к ошибке выполнения скрипта.
set -u
echo "Переменная VAR: $VAR"
./script.sh: line 3: VAR: unbound variable
Или так:
set -o noexec
Включает режим noexec (режим «нет выполнения»). Когда этот режим включен, скрипт не будет выполняться, а только проверяться на синтаксические ошибки. В общем много вариантов есть, чтобы проверить не запуская.
А завтра кстати внеплановая пятница! Ждем ждем…
tags: #bash
—
💩 @bashdaysРеклама. ООО «Отус онлайн-образование», ОГРН 1177746618576var=$(command)
от
var=`command`
Держу пари многие, используют оба варианта даже не задумываясь. Порой в скриптах я могу одновременно встретить как первую, так и вторую конструкцию. И аргумент всегда один - но оно же работает, чо душнишь?
Кстати если в Bash скрипте видишь, что используются обе конструкции, высока вероятность, что скрипт писался копипастой из разных источников.Если на чистоту, то эти два примера ничем не отличаются, логика работы будет одинакова. Выполнится какая-либо команда (command) и ее результат запишется в переменную var.
Эти штуки, кривые кавычки, называются backticks = обратные палки/знаки или gravemarks = надгробия.Второй вариант с надгробиями звучит как-то прикольнее, чем какие-то палки-копалки. А в чём, тогда подвох? Да всё просто - gravemarks (надгробия) морально устарели и в бест-практиках нынче $(command). Ну и конечно же при использовании надгробий будет много геморроя например с вложенными командами и экранированием. Вот пример:
list=$(ls -l $(cat bashdays.txt))
либо
list=`ls -l \`cat bashdays.txt\``
Очевидно что первый вариант предпочтительнее, чем второй. Предпочтительнее в плане читаемости и ошибку в синтаксисе ты вряд ли допустишь. Ведь чем сложнее будет команда, тем больше этих надгробий придётся использовать. Возникает проблема вложенности.
Ну и порой использование gravemarks влияет на результаты:
A="A_VAR"
echo $(echo "\$A")
echo `echo "\$A"`
Вроде две одинаковые команды, а выведут разный результат:
$A
A_VAR
Обратная косая черта внутри надгробий обрабатывается неочевидным образом. Но если пропатчить таким образом вторую команду, то все становится на свои места:
echo `echo "\$A"`
В результате получим вывод на экран: $A как и в первом варианте с $().
Вот такие пироги. Так что если до сих пор используешь gravemarks, посмотри в сторону $(), оно как бы правильнее. Но опять же, со своим уставом куда-то там не ходят. Выбор каждого.
Ладно. Давай краба, увидимся!
tags: #bash
—
💩 @bashdaysecho "Hello Bashdays Welcome"
Если его выполнить, то на экран выведется ожидаемый результат. Но что если я хочу добавить логику в этот скрипт, которая будет детектить pipe и отдавать другой ожидаемый результат?
Давай по порядку, смотри:
bold=$(tput bold)
normal=$(tput sgr0)
echo "Hello ${bold}BashDays${normal} Welcome"
Этот скрипт выведет строку Hello Bashdays Welcome, где слово Bashdays будет жирным (bold).
Дальше я хочу, чтобы при использовании pipe, слово BashDays НЕ выделялось жирным. То есть если запустить скрипт самостоятельно, без использования пайпов, то текст будет жирным. Если скрипт запущен через пайп, то текст остается нормальный.
./script.sh Hello Bashdays Welcome ./script.sh | cat Hello Bashdays WelcomeПригодится например, чтобы не передавать утилитам всякие ненужные управляющие символы, а на экран выводить опрятную и ухоженную девочку. Про управляющие символы можешь почитать в этом посте. Давай мутить В POSIX оболочках существует флаг -t, с помощью которого можно задетектить файловый дескриптор. Если файловый дескриптор fd = True, то скрипт запущен самостоятельно в терминале. Дескриптор fd может принимать значения:
0 - стандартный ввод 1 - стандартный вывод 2 - стандартная ошибкаПишем скрипт с логикой:
bold=$(tput bold)
normal=$(tput sgr0)
if [ -t 1 ] ; then
echo "Hello ${bold}BashDays${normal} Welcome"
else.
echo "Hello BashDays Welcome"
fi
Запускаем:
./script.sh Hello BashDays Welcome ./script | cat Hello BashDays WelcomeВидим сработал детект. Скрипт самостоятельно определил, как он был запущен и вывел на экран, то что требуется. Флаг -t справедлив не только для Bash, он будет работать во всех POSIX оболочках. Вот такие вот интересные кейсы порой приходится решать, благо всё это давно придумано за нас, главное знать про эту срань. Ну теперь и ты про неё знаешь. Хорошего дня, увидимся! tags: #bash — 💩 @bashdays
false | true
Если после её выполнения сделать: echo $?, то получим статус выхода 0. То есть у нас есть статус завершения именно команды true. НО теперь узнать с каким кодом завершилась первая команда false?
А вот так, модифицируем команду:
false|true ; ( exit ${PIPESTATUS[0]} )
В этом случае будет получен код завершения 1, который справедлив для команды false. Как ты можешь заметить, если в PIPESTATUS изменить [0] на [1], мы снова получим статус завершения 0 (то есть для true).
PIPESTATUS - это специальный массив переменных в оболочке Bash, который содержит статус завершения каждой команды в цепочке команд, выполняемой через конвейер (pipeline). Когда в вашем скрипте используется конструкция конвейера с несколькими командами, PIPESTATUS сохраняет статус завершения каждой из этих команд.Аналогично можно манипулировать любой частью пайпа и получать нужные тебе статусы.
command1 | command2 | command3
echo "${PIPESTATUS[0]}"
echo "${PIPESTATUS[1]}"
echo "${PIPESTATUS[2]}"
Есть еще много велосипедов для реализации желаемого, но этот коробочный самый распространенный и понятный. Изучай!
Больше про коды завершения, можешь найти в этом посте.
tags: #bash
—
💩 @bashdaysgrep -a -C 500 -F 'console import:reports' /dev/sda1
Соответственно console import:reports это часть таски, которая когда-то выполнялась. Команда отработала и нашла мне удаленный cron файл. Копипаста и все рады. Но тут опять же повезло, что данные не перезаписались на диске.
В любом случае можно было бы восстановить по хронологии из файла /var/log/cron, но уже с геморроем. Либо грепнуть CRON в syslog и journald.
На будущее сделал клиенту алиас:
alias crontab="crontab -i"
Этакая защита от дурака и толстых пальцев. Теперь если перепутать «e» и «r», то оно выругается и запросит подтверждение:
crontab: really delete root's crontab? (y/n)
Ну и на будущее, не забывай делать бэкапы. Хотя сколько не говори, никто не делает, пока петух не клюнет. Хотя сколько меня не клевал, я все равно зачастую забиваю, видимо уверен, что смогу всё восстановить.
Бэкап крона:
@daily crontab -l > $HOME/.crontab
Восстановить:
crontab < $HOME/.crontab
А вообще часто советуют использовать systemd timers вместо cron или systemd-cron, мол они гибче и безопаснее. Ну хз хз, каждый привык выбирать свои игрушки.
Всех с пятницей, хороших предстоящих выходных. Увидимся!
tags: #bash #linux
—
💩 @bashdayssudo chattr +i /var/log
Перезагружаемся. Хе! Сработало. В папке пустота! На этом можно двигать задачу в done.
После установки этого атрибута, у тебя начнет сыпать ошибками apt, но пакеты оно будет ставить, так что имей это ввиду. Это фиксится добавлением строчки Dir::Log "/путь"; в apt.conf.d.
НО, не все так просто. Если ввести:
jorunalctl -f
Мы снова видим логи. Но теперь уже на экране. А раз они на экране, значит они где-то лежат и на диске. Давай искать.
Зачищаем этот системный журнал:
sudo journalctl --rotate && sudo journalctl --vacuum-time=1s
Ага, на экране видим путь, то что оно зачистило, это /run/log/journal/.
Зачистка это хорошо, но логи все равно будут писаться. Надо отключить эту шляпу на глобальном уровне.
Открываем файл /etc/systemd/journald.conf и добавляем:
Storage=none
Перезагружаем службу и удаляем остатки:
sudo systemctl restart systemd-journald
rm -R /run/log/journal/*
Теперь если ввести:
jorunalctl -f
Получим такое сообщение: No journal files were found. Прекрасно!
Так, если в системе установлен rsyslog или подобное, то дизейблим и его:
systemctl stop rsyslog
systemctl disable rsyslog
Основное сделали. Осталось логирование в .bash_history. Зачищаем и накидываем атрибут на запрет записи:
cat /dev/null > ~/.bash_history
sudo chattr +i ~/.bash_history
history -c && history -w``
Либо добавляем в .bashrc строчку: HISTSIZE=0, с ней будет нативнее и без костылей с атрибутом.
Делаем финальную перезагрузку. Ну вот и всё, задача выполнена. Система зачищена от логирования. По любому я что-то упустил, поэтому жду с нетерпением твои комментарии.
Вообще все эти способы очень грубые и топорные. Было бы намного изящнее сделать решение, которое удаляет из логов только то, что нужно удалить. Логирование работает в штатном режиме, но критичные данные в логи не попадают.
Всех обнимаю, хорошего дня!
Прошу отметить, что предоставленная здесь информация предназначена исключительно для образовательных и информационных целей. Я не призываю и не одобряю незаконные действия, и использование этой информации для незаконных целей запрещено. Читатели должны соблюдать законы своей страны и использовать свои навыки с уважением к этическим нормам и законам.
tags: #bash #linux
—
💩 @bashdays$1/$2/$3 - позиционные параметры переданные в скрипт $# - количество переданных аргументов $- - текущие флаги оболочки $$ - идентификатор процесса PID $_ - последний аргумент предыдущей команды $IFS - разделение слов во входящих данных $? - код возврата последней выполненной команды $! - PID последнего запущенного в фоновом режиме процесса $0 - имя исполняемого файлаТеперь на примерах Позиционные параметры
echo "Первый аргумент: $1"
echo "Второй аргумент: $2"
echo "Третий аргумент: $3"
./script.sh one two three
Выведет:
Первый аргумент: one
Второй аргумент: two
Третий аргумент: three
Количество переданных аргументов
echo "Количество аргументов: $#"
./script.sh one two three
Выведет:
Количество аргументов: 3
Текущие флаги оболочки
echo "Текущие флаги оболочки: $-"
Выведет:
Текущие флаги оболочки: himBHs
Про эти флаги можешь почитать в этом посте.
Идентификатор процесса PID
echo "PID текущей оболочки: $$"
Выведет:
PID текущей оболочки: 362748
Последний аргумент предыдущей команды
echo "Hello BashDays"
echo "Welcome Home"
echo "Последний аргумент предыдущей команды: $_"
Выведет:
Последний аргумент предыдущей команды: Welcome Home
Разделение слов во входящих данных
IFS=","
read -ra words <<< "hello,bashdays,how,are,you"
for word in "${words[@]}"; do
echo "Слово: $word"
done
По умолчанию у $IFS это пробел, но можно заменить на что-то другое. Тут указываем, что разделитель у нас будет «запятая».
Вывод будет таким:
Слово: hello
Слово: bashdays
Слово: how
Слово: are
Слово: you
Код возврата последней выполненной команды
ls non_existent_file.txt
echo "Код возврата: $?"
Выведется:
ls: cannot access 'non_existent_file.txt': No such file or directory
Код возврата: 2
PID последнего запущенного в фоновом режиме процесса
sleep 10 &
echo "PID последнего запущенного в фоновом режиме: $!"
Выведет:
PID последнего запущенного в фоновом режиме: 362786
Имя исполняемого файла
echo "Имя этого скрипта: $0"
Выведет:
Имя этого скрипта: -bash
А если выполнить из скрипта, то оно покажет его имя.
Вроде всё упомянул, если что-то упустил, добавляйте в комментарии.
tags: #bash
—
💩 @bashdaysecho "Изучаем \$@"
for arg in $@; do
echo "Аргумент: $arg"
done
Запускаем так:
./script.sh 1 2 '3 with spaces'
На экран выводится:
Изучаем $@
Аргумент: 1
Аргумент: 2
Аргумент: 3
Аргумент: with
Аргумент: spaces
Видим что каждый аргумент у нас вывелся отдельным словом. Даже несмотря на то, что последний аргумент у нас заключен в кавычки и является строкой с пробелами. В этом и особенность. Он наплевал на кавычки и разбил строку на дополнительные аргументы.
А теперь немного переделаем скрипт (добавим кавычки) и запустим с теме же аргументами:
echo "Изучаем \$@ с кавычками"
for arg in "$@"; do
echo "Аргумент: $arg"
done
В этот раз на экран выведется такое:
Изучаем $@ с кавычками
Аргумент: 1
Аргумент: 2
Аргумент: 3 with spaces
Вот! Тут мы получили уже более ожидаемый результат. Массив: {$1, $2, $3}
Теперь про $*
echo "Изучаем \$*"
for arg in $*; do
echo "Аргументы: $arg"
done
После запуска с такими же аргументами:
Изучаем $*
Аргументы: 1
Аргументы: 2
Аргументы: 3
Аргументы: with
Аргументы: spaces
Получаем то же самое, что и с $@ без кавычек. Шило на мыло. А вот если заключить $* в кавычки:
echo "Изучаем \$*"
for arg in "$*"; do
echo "Аргументы: $arg"
done
На экране мы получим:
Изучаем $*
Аргументы: 1 2 3 with spaces
Вот и отличие. Оно небольшое, оно практически ненужное. Но в этом и кроются детали. Все аргументы объединились в одну строку, отделенным символом пробела, заданным в IFS (пробел по умолчанию).
Если это не знать, то легко можно наступить на грабли.
Вот пример как заменить IFS разделитель (пробел на запятую):
echo "Изучаем \$*"
IFC=","
for arg in "$*"; do
echo "Аргументы: $arg"
done
Вывод будет таким:
Изучаем $*
Аргументы: 1,2,3 with spaces
Как «эффект бабочки», забыл поставить кавычки, сломал в будущем логику. Поставил кавычки, сломал другую логику. Куда не ткни, вечно какие-то костыли.
Я конечно сомневаюсь, что ты пользуешься таким высокоуровневым программированием (я не пользуюсь), но порой бывает натыкаешься на чужие скрипты с подобной логикой. И тогда важно не попасть в просак.
А что такое просак, хорошо объясняется в фильме «Жмурки».
Ладно, не смею тебя больше отвлекать. Увидимся. Хорошего понедельника!
tags: #bash #linux
—
💩 @bashdaysexport var=значение
var=значение
Что тут не так? А тут всё так. Только в первом случае я использую export, чтобы сделать переменную доступной для подпроцессов.
То есть, переменная заданная через export будет доступна для любого процесса, который будет запущен в рамках процесса оболочки.
А если задать переменную БЕЗ export, то переменная будет доступна только в самой оболочке и недоступна для других процессов.
Скучная теория, давай на практике:
var1="Hello Bashdays"
echo $var1
Hello Bashdays
Запускаем, смотрим, ага переменная задается без экспорта и выводится на экран. Идем дальше и запускаем это:
export var1
var2="Bye, Bye"
bash
echo $var1
Hello Bashdays
echo $var2
1. Экспортируем var1
3. Объявляем вторую переменную var2
4. Запускаем bash в bash (подпроцесс)
5. Получаем значение переменной var1
6. Получаем значение переменной var2
В пятом пункте переменная var1 вывелась на экран даже после того, как я запустил еще одну оболочку bash внутри оболочки bash. Переменная стала доступна для подпроцессов.
А вот var2 которую я объявил во втором пункте, куда-то делась. На самом деле она никуда не делась, просто подпроцесс её не видит. Стоит мне сейчас сделать так:
exit
echo $var2
Bye, Bye
Я получаю значение переменной var2. Если уж совсем грубо говоря, export делает переменную глобальной на уровне подпроцессов.
Ну и частые ошибки, когда пытаешься экспортировать массив:
export Array=("Hello" "Bashdays")
С виду все в порядке и вроде логично, ошибок нет. НО в подпроцессах если попытаться вывести содержимое этого массива, ты получишь пустоту. И люди на это убивают несколько часов, чтобы понять в чем причина.
А причина простая, просто нужно знать — в Bash не существует прямой поддержки экспортирования массивов.Но где наша не пропадала, если уж совсем хочется, то можно преобразовать массив в строку и затем экспортировать эту строку. Делается это так:
Array=("Hello" "BashDays")
export ArrayString="$(printf "%s|" "${Array[@]}")"
IFS='|' read -ra Array <<< "$ArrayString"
echo "${Array[@]}"
Значок собаки = все элементы массива, его можно справедливо заменить на 1, 2, 3 и т.п. А printf используются для форматирования массива в строку, разделяя элементы символом «|».
Теперь массив экспортирован и будет доступен в подпроцессах.
Вообще холиваров на тему экспортирования переменных очень много, каждый топит за своё, но тут уже хозяин барин. Как говорится под каждую задачу своё решение, где-то необходим export, где-то он и не нужен.
Ладно чо, хороших тебе предстоящих выходных и береги себя!
tags: #linux #bash
—
💩 @bashdayscurl ascii.live/parrot
curl ascii.live/forrest
curl ascii.live/can-you-hear-me
curl parrot.live
А тут и тут можешь посмотреть исходники этих проектов. Там еще много подобных curl запросов можно понаделать с котиками, пончиками, бэтменами. А еще можно поставить себе на сервер и устраивать консольные вечеринки.
tags: #linux
—
💩 @bashdaysperl ./pokazisiski.pl
Сам код этого арта, сгенерирован с помощью модуля Acme::EyeDrops. Кстати на странице модуля много примеров с такими картинками (верблюды, морды, заборы, снежинки), каждая выполняет какое-то своё действие + есть примеры генераторов.
А вот эта команда, превратит файл обратно в исходник. То есть выведет исходный код вместо того, чтобы его выполнить.
perl -MO=Deparse ./pokazisiski.pl
Опять же не знаю, что ты будешь делать с этой информацией. Говорят что perl давно мёртв, но в современных скриптах и решениях люди любят его применять. ХЗ почему, наверное потому что perl есть везде. Хотя сейчас глянул, последний релиз был 2024-01-20, версия 5.39.7 (devel), капец, оно живое.
Первое и последнее, что я делал на perl, это был вывод «Hello World», на этом моё знакомство с ним закончилось. Даже на СИ порог вхождения мне легче показался, хотя по СИ у меня была книга, а по perl только какие-то огрызки из фидонета.
Ладно. Побежали дальше мир спасать от криворучек. Увидимся!
А ты применяешь perl в современном мире? Если да, поделись пожалуйста в комментариях, как и где. Очень интересно.
tags: #linux
—
💩 @bashdays
Уже доступно! Исследование Telegram 2025 — ключевые инсайты года 
