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 788 suscriptores, ocupando la posición 5 701 en la categoría Tecnologías y Aplicaciones y el puesto 28 125 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 788 suscriptores.
Según los últimos datos del 18 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de -216, y en las últimas 24 horas de -5, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 22.75%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 12.90% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 5 413 visualizaciones. En el primer día suele acumular 3 068 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 21.
- 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 19 junio, 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.
#!/bin/bash
i=0
for name in `ls *.txt`
do
i=$[i+1]
mv $name `printf "%02d" $i`.txt
done
А проблема заключается в том, что скрипт удаляет файлы, которые содержат в именах цифры.
За качество скрипта тереть не будем. Нам нужно задебажить проблему, ведь явных команд на удаление в этом скрипте — нет.
Для начала, давай создадим вводные, чтобы было с чем работать.
mkdir /tmp/test
cd /tmp/test
touch {001..003}.txt {01..03}.txt
Так, создали 6 файлов с разными именами:
01.txt
02.txt
03.txt
001.txt
002.txt
003.txt
Модифицируем проблемный скрипт, чтобы он выводил строку с командой mv перед её выполнением:
#!/bin/bash
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv $name `printf "%02d" $i`.txt
done
exit 0
Запускаем и получаем красивое:
DEBUG: mv 001.txt 01.txt
DEBUG: mv 002.txt 02.txt
DEBUG: mv 003.txt 03.txt
DEBUG: mv 01.txt 04.txt
DEBUG: mv 02.txt 05.txt
DEBUG: mv 03.txt 06.txt
Смотрим что у нас осталось, хуяк и вместо 6 файлов, у нас осталось всего лишь 3 штук.
ls
04.txt
05.txt
06.txt
Куда блядь делось остальное? Вокруг да около ходить не будем.
Дело в том, что в одном каталоге не может быть двух файлов с одинаковым названием. Очевидно? Да!
Как работает mv:
mv <старое имя> <новое имя>
Если <новое имя> это существующее имя файла, тогда оно будет удалено, а <старое имя> получит имя <новое имя>.
ㅤ
Если взять нашу ситуацию, то произошло следующее:
1. DEBUG: mv 001.txt 01.txt
2. DEBUG: mv 002.txt 02.txt
3. DEBUG: mv 003.txt 03.txt
4. DEBUG: mv 01.txt 04.txt
5. DEBUG: mv 02.txt 05.txt
6. DEBUG: mv 03.txt 06.txt
1. Удалено 01.txt. и 001.txt -> 01.txt
2. Удалено 02.txt. и 002.txt -> 02.txt
3. Удалено 03.txt. и 003.txt -> 03.txt
✔ Итого потеряли 3 файла, печаль.
Теперь давай более наглядно. Удаляем подопытные файлы и создаем новые. Плюс запишем в каждый файл его имя.
for i in {001..003}.txt {01..03}.txt; do echo $i > $i;done
Получилось 6 файлов, в каждом файле содержится имя самого файла.
Смотрим что получилось:
ls -la
8 Jul 10 08:05 001.txt
8 Jul 10 08:05 002.txt
8 Jul 10 08:05 003.txt
7 Jul 10 08:05 01.txt
7 Jul 10 08:05 02.txt
7 Jul 10 08:05 03.txt
Снова модифицируем скрипт и задаем опцию -b для утилиты mv. Теперь для существующего файла будет создаваться резервная копия. Бекап ёпта!
#!/bin/bash
i=0
for name in `ls *.txt`
do
i=$[i+1]
echo 'DEBUG: mv' $name `printf "%02d" $i`.txt
mv -b $name `printf "%02d" $i`.txt
done
exit 0
Выполняем скрипт и смотрим что у нас осталось от файлов.
ls -la
7 Jul 10 08:05 01.txt~
7 Jul 10 08:05 02.txt~
7 Jul 10 08:05 03.txt~
8 Jul 10 08:05 04.txt
8 Jul 10 08:05 05.txt
8 Jul 10 08:05 06.txt
Теперь смотрим содержимое самих файлов и преисполняемся.
По итогу, если нужно пронумеровать файлы, позаботься об уникальности имён и заранее протестируй на тестовых данных. Чтобы случайно к херам не снести все ассетсы с продакшена.
Ну и всегда обрабатывай исключения если файл уже существует, чтобы не остаться без штанов.
Изучай, всего тебе самого наилучшего!
tags: #linux #bash
—
🔔 @bashdaysdd if=/dev/zero of=/dev/null status=progress
И наблюдаем как строка статистики постоянно меняется.
В простонародье — рефрешится.
4254480896 bytes (4.3 GB, 4.0 GiB) copied, 4.77966 s, 890 MB/s
Бесполезная? Ну не скажи, с её помощью можно протестировать скорость чтения и записи.
ㅤ
Эта команда копирует блоками нули из спец-файла в другой. Записывая статистику на стандартный вывод ошибок stderr.
Строка статистики постоянно меняется. Но как dd это делает? Давай разберемся ниже.
Запускаем страшную кишку и немного ждем
dd if=/dev/zero of=/dev/null status=progress |& { read -t 4; echo "${REPLY@Q}"; }
По итогу получаем, что-то подобное:
$'\r927888896 bytes (928 MB, 885 MiB) copied, 1 s, 928 MB/s\r1851585024 bytes (1.9 GB, 1.7 GiB) copied, 2 s, 926 MB/s\r2749514752 bytes (2.7 GB, 2.6 GiB) copied, 3 s, 917 MB/s'
Портянка с read означает — ждем 4 секунды, считываем первую строчку ввода и сохраняем ее в переменную REPLY, а далее выводим содержимое переменной на экран.
✔ Не так уж и страшно, правда?
Ааа, херовина |& нужна, чтобы объединить stdout и stderr и затем направить его в read.
Ну дак вот.
Получается утилита dd, пишет статистику за секунду в стандартный поток ошибок и затем суёт символ «\r».
Который означает - возврат каретки.
Вот и вся магия.
Делаем хак, чтобы избавиться от возврата каретки:
dd if=/dev/zero of=/dev/null status=progress |& tr '\r' '\n'
Теперь статистика выводится в столбик:
1836032000 copied, 2 s, 918 MB/s
2793336832 copied, 3 s, 931 MB/s
3726170624 copied, 4 s, 932 MB/s
4655933440 copied, 5 s, 931 MB/s
Нагляднее? Конечно!
Что с этим можно сделать? Ну например протестировать скорость чтения и записи, используя разный размер буферов.
Херачим Bash скрипт:
#!/bin/bash
for i in {9..20}
do
bs=$((2**i))
dd bs="$bs" if=/dev/zero of=/dev/null status=progress |& {
mapfile -d$'\r' -n 2; printf '%-8s : %s\n' "$bs" "${MAPFILE[1]}"; }
done
exit
Запускаем и получаем:
512 : 878647296 copied, 1 s, 879 MB/s
1024 : 1618295808 copied, 1 s, 1.6 GB/s
2048 : 3096260608 copied, 1 s, 3.1 GB/s
4096 : 5056294912 copied, 1 s, 5.1 GB/s
8192 : 8047247360 copied, 1 s, 8.0 GB/s
Круто? Да охуеть!
Ну а если нужна статистика за пять секунд. Делаем 5+1 к количеству записей и индексу массива. Массив начинается с нуля. Но можешь это изменить с помощью опции -O.
#!/bin/bash
for i in {9..20}
do
bs=$((2**i))
dd bs="$bs" if=/dev/zero of=/dev/null status=progress |& {
mapfile -d$'\r' -n 6; printf '%-8s : %s\n' "$bs" "${MAPFILE[5]}"; }
done
exit
По итогу имеем статистику за 5 секунд:
512 : 4667945472 copied, 5 s, 934 MB/s
1024 : 8693190656 copied, 5 s, 1.7 GB/s
2048 : 15988561920 copied, 5 s, 3.2 GB/s
4096 : 27510878208 copied, 5 s, 5.5 GB/s
8192 : 39937343488 copied, 5 s, 8.0 GB/s
Вот такие пироги. Теперь можешь тестировать VPSки и писать статью на хабру с бенчмарками.
Давай, увидимся завтра!
tags: #linux #bash
—
🔔 @bashdaysПривести в порядок всё, чтоб не стыдно перед последователями было. А доступы и так оставят и почту. Ещё и докучают что-либо сделать. А мы идём на большие деньги смотреть как у других всё сделано не так как нужноНа втором месте Александр Смирнов (wddingo) с 38 лайками за комментарий:
Самое гадкое, что можно было им сделать - я сделал, я ушел с этой работы. Пусть теперь сами с этим говном разбираются. А пакостить - это считаю слишком мелочнымНу и третье место занял Максим (Rosssich), набрав 30 респектов за комментарий:
а вообще - ключевое слово "не заплатили". поэтому заявление в трудовую инспекцию, суд и т.д. там можно урона нанести больше чем от всяких там chaosmonkey )На этой неделе к вам придет Макс и узнает куда засылать призы. Ждите. Нюансы. На руках у меня всего одна колода, она отправится в приоритете Алексею, остальные две вышлем в ближайшее время, как получим их от организатора. Ну либо закинем рублей на карту, чтобы вы самостоятельно заказали их через Авито и не парились на почте. Всем спасибо за участие, вы все лучшие!
CTRL+C я напишу в следующих постах.
ㅤ
Сегодня что-то вроде на изи потыкать. Понедельник как-никак.
Где применяется реврайт CTRL+C? Да практически нигде, но то, что его можно зареврайтить это факт.
✔ А вообще причины для реврайта разные:
- Избежание конфликтов с программным обеспечением
- Удобство и предпочтения пользователя
- Специальные рабочие процессы
- Случайные нажатия
- Ограниченные возможности клавиатуры
- и т.п.
Давай теперь на практике.
Смотрим дефолтное сочетание
stty -a
stty - утилита, которая настраивает параметры терминала.Получаем портянку вида:
speed 38400 baud; rows 57; columns 237; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0; -parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
Смотрим параметр intr, он и указывает на сочетание, которое будет слать SIGINT. По умолчанию как видим оно = CTRL+C.
Давай теперь поменяем эту штуку:
stty intr ^K
Теперь запускаем для теста sleep 1000 и пробуем нажать CTRL+C. Опа и нихуя не происходит.
Поздравляю, реврайт прошел успешно. А теперь нажми CTRL+K и sleep завершится. Клёва? Клёва!
Можешь теперь издеваться над коллегами и ломать им рабочие процессы.
Чтобы сохранить эти настройки, выполняем:
echo 'stty intr ^K' >> ~/.bashrc
Ну либо в .zshrc, тут уже сам смотри где у тебя первоначальные настройки валяются.
А можно сделать CTRL+ALT+K ?
Через stty увы такое провернуть не получится.
Но используя xbindkeys такое возможно.
vim ~/.xbindkeysrc
и добавить туда:
"pkill -SIGINT -f ."
Control+Alt + k
Но xbindkeys со своими подводными камнями, оно для Иксов. А если у тебя Wayland то смотри в сторону sway или gnome-keybinding.
Такие дела. Ладно, пошли дальше мир спасать. Хорошего понедельника!
tags: #linux
—
🔔 @bashdays1. ps -o rss -p $$
2. var=$(printf "%s\n" {1..100000})
3. ps -o rss -p $$
4. var="bashdays"
5. ps -o rss -p $$
В первой строчке выводится количество физической памяти в килобайтах, используемой текущим процессом оболочки. У меня выдало 4348.
Во второй строчке записываем в переменную var содержимое от 1-100000 с переносом на новую строку.
В третьей строчке снова выводим количество физической памяти, у меня 5276.
Ага, оно стало больше. Было 4348 стало 5276.
В четвертой строчке, перезаписываем var на строку «bashdays» и снова проверяем память.
На этот раз пятая строчка вновь показывает: 4348.
Что и требовалось доказать. Использование памяти увеличивается, а затем снова уменьшается.
Все манипуляции с памятью выполняются на уровне Linux ядра. И нет никакой необходимости рвать жопу и греть голову.
Bash не работает с памятью и объектами, так же как обычные языки программирования.
tags: #bash
—
🔔 @bashdaysSHOW SLAVE STATUS\G;
А там ошибки брынчат, как хуи в бидоне.
Error 'Cannot delete or update a parent row: a foreign key constraint fails'
Ну это ладно, ошибка и ошибка, тут понятно что делать — НИЧЕГО.
Так как реплика используется чисто аналитиками на потыкать, игнорим эту ошибку через my.cfg. Быстрофикс.
✔ Для продуктовой реплики такое делать - НЕЛЬЗЯ!
Самый важный вопрос — какого хера мониторинг 3 месяца молчал?
Тут уже интереснее. Реплика заведена в prometheus, экспортеры есть, все дела.
Но из графаны сервер пропал, хотя год назад я ее точно там видел.
Думаем… Думаем… Смотрю графану, мониторится поле: replica_seconds_behind
Хм, лезу обратно на реплику, а там сроду нет mysql_exporter. Что же это тогда такое?
Копаем вглубь и видим, что node_exporter мониторит папку /tmp на наличие файликов .prom.
Ага… То есть метрики с mysql реплики собирает какой-то bash скрипт по крону, генерит текстовичок и отдает в prometheus.
Типа такого:
replica_slave_io_running{host="replica"} 1
replica_seconds_behind{host="replica"} 1234567
Да, оно прекрасно работало, до момента пока не вылезла ошибка: Cannot delete or update a parent row
Соответственно текстовый файл replica.prom получился в таком формате:
replica_slave_io_running{host="replica"} 1
replica_seconds_behind{host="replica"} Null
Ну а дальше prometheus такое распарсить не смог (он хочет циферки, а не буковки) и тихонечко вывел эту ноду из графаны и вообще отовсюду. Ну и аллерты в придачу. На что им тригериться если ноды нет нигде?
Прикол в том, что во время возникновения ошибки, поле Seconds_Behind_Master в mysql принимает значение Null, а не продолжает дальше считать на сколько отстала реплика.
ㅤ
А вот и bash скрипт, который собирал метрики:
#!/bin/bash
MAINDIR=/tmp
METRICS=rstatus.prom
HOST="replica"
SLAVE_IO_RUNNING=$(mysql -e 'SHOW SLAVE STATUS \G' | grep 'Slave_IO_Running'| awk '{print $2}')
SLAVE_SECONDS_BEHIND=$(mysql -e 'SHOW SLAVE STATUS \G' | grep 'Seconds_Behind_Master'| awk '{print $2}')
if [[ "$SLAVE_IO_RUNNING" == "Yes" ]]; then
J=1
echo 'replica_slave_io_running{host="'$HOST'"}' $J > $MAINDIR/$METRICS
else
J=0
echo 'replica_slave_io_running{host="'$HOST'"}' $J > $MAINDIR/$METRICS
fi
echo 'replica_seconds_behind{host="'$HOST'"}' $SLAVE_SECONDS_BEHIND >> $MAINDIR/$METRICS
Работа над ошибками проведена, инцидент разобран. Ни одна жопа на ретроспективе пока не пострадала, но возможно дело времени.
Как писать подобные экспортеры я накидывал в этом посте.И всегда помни — если изобретаешь велосипед, всегда обрабатывай эксепшены! tags: #devops #debug #bash #monitoring — 🔔 @bashdays
a=''; while read -n 1 -s c; do [ "$c" = '' ] && break; echo -n '*'; a="$a$c"; done
Согласен, выглядит хуёва, но вторая на визуал еще хуёвие.
В этой кишке есть нюанс, она НЕ работает с кнопкой Backspace, то есть ты не сможешь отредактировать, то что уже ввел.
Кишка вторая.
a=''; while read -n 1 -s c; do [ "$c" = '' ] && break; if [ "$c" = $'\x7f' ]; then [ "$a" != "" ] && { a=${a:0:-1}; printf "\b \b"; }; else echo -n '*'; a="$a$c"; fi; done;
А тут уже более совершенный вариант, с рабочей кнопкой Backspace.
ㅤ
Чо делать дальше? Ну логично же, в переменную «a» записывается введенный пароль, а дальше делай с ним все что захочешь.
Вот такие пироги!
tags: #bash
—
🔔 @bashdaysapt update && apt -fy upgrade && [ -f /var/run/reboot-required ] && shutdown -r now
Первая часть понятна, обновили, апгрейднули, а дальше идет магия.
ㅤ
В квадратных скобках конструкция, которая проверяет наличие файла /var/run/reboot-required и если он есть, то выполняется перезагрузка.
Файл reboot-required создается, если система считает, что после обновления требуется перезагрузка. Если файл существует, возвращается true. А если true, то запускается -r = reboot.
Вот и вся наука. Изучай…
tags: #bash #linux
—
🔔 @bashdaysseq 1000 | datamash sum 1
datamash sum 1 < <(seq 1000)
➡️ Awk
seq 1000 | awk '{sum += $1}END{print sum}'
awk '{sum += $1}END{print sum}' <(seq 1000)
➡️ Dc
seq 1000 | dc -e '0d[?+2z>a]salaxp'
dc -e '0d[?+2z>a]salaxp' < <(seq 1000)
Последнее решение самое занимательное. И самое небезопасное, потому, что данные управляют программой.
Вообще во всех трёх случаях очень желательно очищать входные данные.
Почитать:
man datamash
man seq
man awk
man dc
Да будет так!
tags: #bash
—
🔔 @bashdaysЛибо хуй пополам, либо пизда в дребезги, другого не дано.А еще соль, что при обновлении мне приходится параллельно поднимать клон сервера, на случай если обновка сделает мозги, можно будет на старую версию переключиться. Удобство через край… И на закуску фаталити — обновку накатывать нужно поздно ночью, либо в выходные, когда разработчики в режиме AFK. Хуита какая-то! Можно и в будни, но заебут вопросами — а когда заработает?
Короче я такие апдейты на хую Лунтика вертел.Хоть отдельного человека нанимай, чтобы каждый день обновки катил в три часа ночи. Что я буду с этим делать? НИЧЕГО! Пусть нахуй всё взламываеют и пиздят, заебали! Пардон. Накипело! ps: так, мне еще вам конкурс придумывать сегодня, так что чуть позже увидимся, пойду успокоительное пить да Зельду на свиче задрачивать. tags: #рабочиебудни — 🔔 @bashdays
eval довольно пространное описание. Давай сегодня с ней поковыряемся.
Прежде чем выполнить какой-то код, интерпретатору требуется обработать его.
Нужно найти команду, аргументы, различные перенаправления. Расширить параметры, фигурные скобки, тильда, переменные подстановка результатов выполнения команд, математика, арифметика, география, литература, подстановка имен файлов и т.п.
Короче дохера всего. Полный список можешь найти в разделах SIMPLE COMMAND EXPANSION и EXPANSION выполнив:
man 1 bash
Ну дак вот, команда eval запускает эту обработку кода. Но прежде чем команда eval будет запущена, код должен быть обработан.
На вход eval попадает код, который уже обработан и прошел проверку. Поэтому получается, что код обрабатывается два раза и только после этого выполняется.
Очевидно? Совсем нет. Но вот оно так работает, чо с него взять.
Эта возможность используется по-разному, например для генерации и последующего исполнения кода.
Также если знать некоторые последовательности обработки кода, можно сгладить некоторые особенности самой обработки.
Ниже примеры. При присваивании значения переменной, приведенная конструкция с фигурными скобками не расширяется.
Расширение переменной в строке с командой echo происходит гораздо позже, чем расширение фигурных скобок.
Поэтому используем eval. Первая обработка развернет переменную. Вторая, которая запустит eval, развернет конструкцию с фигурными скобками.
str={1..10}
echo $str
eval echo $str
В результате получим:
{1..10}
1 2 3 4 5 6 7 8 9 10
Другой пример. Здесь из-за использования одинарных кавычек, переменной присваивается указанная строка, а не значение переменной.
И обработка строки делает расширение переменной один раз. Получаем значение переменной s0. Количество обработок строки равно количеству eval плюс один.
(s0=100 ; s1='$s0' ; s2='$s1' ; echo "$s2")
(s0=100 ; s1='$s0' ; s2='$s1' ; eval echo "$s2")
(s0=100 ; s1='$s0' ; s2='$s1' ; eval eval echo "$s2")
На экране ты увидишь:
$s1
$s0
100
А теперь более сложный пример. Если слева от оператора перенаправления указана не корректная строка, в этом случае для перенаправления будет использована только часть строки, всё что находится слева не будет учитываться.
Это значит, что если ты откроешь файл на чтение и слева укажешь переменную содержащую нужный дескриптор, эта переменная не будет участвовать в перенаправлении.
И раз не указан дескриптор, файл будет ассоциирован со стандартным потоком ввода.
Решение. Делаем так, чтобы при первой обработке перенаправление было распознано как обычная строка и была расширена переменная. Тогда на второй обработке слева от оператора перенаправления будет стоять номер дескриптора из переменной.
(fd=6 ; exec "$fd"</dev/null)
(fd=6 ; eval exec "$fd</dev/null")
(fd=6 ; eval exec "$fd"\</dev/null)
(fd=6 ; eval exec "$fd"'</dev/null')
В первой строке: /dev/null на stdin и пытается запустить 6.
Круглые скобки использовались, чтобы не засорять текущую оболочку. И чтобы она не завершилась на первом примере. Из-за того что на её стандартный ввод повесили /dev/null. Проверяем какой дескриптор ассоциирован с устройством /dev/null.
bash -c 'shopt -s execfail ; fd=6 ; exec $fd</dev/null ; ls -l /proc/$$/fd'
bash -c 'shopt -s execfail ; fd=6 ; eval exec $fd\</dev/null ; ls -l /proc/$$/fd'
bash -c 'shopt -s execfail ; fd=6 ; eval exec "$fd</dev/null" ; ls -l /proc/$$/fd'
Этот «хак» с перенаправлением можно использовать для исследований. Когда требуется определённый диапазон дескрипторов.
Почитать: man 1 bash, разделы:
# REDIRECTION # SHELL BUILTIN COMMANDS -> eval # SHELL BUILTIN COMMANDS -> exec # SHELL BUILTIN COMMANDS -> shoptА завтра запускаем конкурс с картами. Моя ленивая жопа соизволила добраться до почты. Хороших выходных! tags: #bash — 🔔 @bashdays
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
