Bash Days | Linux | DevOps
Авторский блог от действующего девопса Самобытно про разработку, devops, linux, скрипты, сисадминство, техдирство и за айтишную жизу. Автор: Роман Шубин Реклама: @maxgrue MAX: https://max.ru/bashdays Курс: @tormozilla_bot Блог: https://bashdays.ru
نمایش بیشتر📈 تحلیل کانال تلگرام Bash Days | Linux | DevOps
کانال Bash Days | Linux | DevOps (@bashdays) در بخش زبانی روسی بازیگری فعال است. در حال حاضر جامعه شامل 23 788 مشترک است و جایگاه 5 702 را در دسته فناوری و برنامهها و رتبه 28 099 را در منطقه روسيا دارد.
📊 شاخصهای مخاطب و پویایی
از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 23 788 مشترک جذب کرده است.
بر اساس آخرین دادهها در تاریخ 19 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر -226 و در ۲۴ ساعت گذشته برابر 1 بوده و همچنان دسترسی گستردهای حفظ شده است.
- وضعیت تأیید: تأیید نشده
- نرخ تعامل (ER): میانگین تعامل مخاطب 23.40% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 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)، کانال همواره بهروز و دارای دسترسی بالاست. تحلیلها نشان میدهد مخاطبان بهطور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامهها تبدیل کردهاند.
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
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
—
🟢 Подпишись: @bashdaysplugins=(git zsh-syntax-highlighting zsh-autosuggestions ssh-agent) zstyle :omz:plugins:ssh-agent agent-forwarding on zstyle :omz:plugins:ssh-agent identities home_rsa work_rsa2 her_rsa3 zstyle :omz:plugins:ssh-agent lifetimeВернемся в теме - кто быстрее. Очевидно же что bash! Давай убедимся. Запустим этот скрипт в bash:
for i in $(seq 1 1000); do bash -c ":" ; doneЗапускаем через time:
time bash speed.sh
time - оценивает по времени производительность любой задачи, выводя после её завершения затраченное время: реальное, пользователя и системы. Через time можешь оценивать производительность по времени любых своих скриптов.
После запуска получаю: 0m1.242s
Скрипт отслеживает точное время открытия шелла 1000 раз без выполнения каких-либо операций.
Ок, теперь давай запустим этот скрипт в zsh: time zsh speed.sh
Результат: 0m1.344s
Не такие и большие различия. Даже можно сказать ничтожные. Но это всего лишь один тест. Чтобы получить какие-то средние значения воспользуемся утилитой:
🐱 shellbench
Устанавливаем и запускаем:
git clone https://github.com/shellspec/shellbench.git . shellbench -s bash,zsh sample/*По итогу получаем около 28ми тестов. На картинке можешь глянуть мои результаты. Числа в таблице это - количество выполнений в секунду. Хм, в совокупности тестов получается что zsh где-то прям намного шустрее, да даже ни где-то, а прям почти лидирует в производительности. По большей части это писькомерство, ну работает оно и работает, какая разница насколько быстрее. Да, соглашусь, но пару раз встречал ребят которым ставили задачу на оптимизацию выполнения скриптов, где важна каждая секунда. Это как у оверклокеров, где каждый герц и фпс это уже победа. Вывод: По результатам тестов, zsh оказался быстрее чем bash, но не везде. Опять-же тут все индивидуально. Кстати когда запускаешь на macos midnight commander + zsh в роле оболочки, то mc запускается прям кое как, секунды три наверное. Поэтому с mc я использую bash, можно конечно этот момент отдебажить, но мне лень. Если когда-нибудь руки дойдут, напишу как пофиксил. А вообще самая быстрая оболочка это Dash (Debian Almquist Shell). Это POSIX-совместимая реализация Bourne Shell. Она заменяет /bin/sh в скриптах по умолчанию и обеспечивает улучшенную скорость, потребляя при этом меньше ресурсов. Dash превосходит bash/zsh по производительности, но его нельзя использовать, так как он не предназначен для взаимодействия. ❓ А какую оболочку используешь ты и почему? Кстати всем привет! Надеюсь твои выходные прошли без проишествий. Увидимся! tags: #linux #utils — 🟢 Подпишись: @bashdays
apt/brew install tty-share либо затаскиваем бинарник с гитхаба.
После установки вбиваем и запускаем:
tty-share --public --readonlyполучаем такое:
public session: https://on.tty-share.com/mbeD30O8tEoWr4_4/ local session: http://localhost:8000/s/local/ Press Enter to continue!Жмем Enter и погнали! Первая строчка public session, она мне и нужна. Копирую URL и отдаю стажерам, они вбивают этот URL в браузер и видят мою расшаренную консоль. Все что я ввожу и запускаю в своей консоли, отображается у стажеров в браузере. Ключ
--readonly делает так, чтобы «прямые руки» моих подопечных не могли вмешиваться в процесс просвещения. Да, если этот ключ не указать, то стажеры смогут через свой браузер вбивать команды в мою консоль.
По моему это офигительно! Работает за любым NAT. Так же есть и локальная сессия (local session), если вы находитесь в одной подсети, можно не указывать ключ --public и довольствоваться локалкой.
У tty-share есть масса других возможностей, можно менять порты, пускать через проксю, указывать оболочку (bash/zsh/etc), писать логи и многое другое. Загляни в хелп (--help) если интересно.
Мне достаточно двух public и readonly, остальное нафиг, работает и хорошо. По безопасности там TLS и https, но разработчик обещает добавить сквозное шифрование, пароли и ключи. Подробнее можешь почитать в разделе docs на странице проекта.
А как завершать сессию? А хуй его знает. В доке есть упоминание про ключ -detach-keys, но из коробки ctrl-o, ctrl-c не работает, либо я совсем буратино. Поэтому когда мне нужно убить public session, я просто сделал себе alias на такую команду:
kill $(ps aux | grep 'tty-share' | awk '{print $2}')
Если разберешься как нативно прикончить сессию, пиши в комменты, скину тебе картинку с котиком.
Залил гифки работы tty-share в телеграф, глянуть можешь тут.
🔵 сайт проекта
🐱 репозиторий на github
алтернативы:
- Instant terminal sharing
- VSCode Live Share
Пользуйся на здоровье. Хорошего тебе дня, увидимся!
tags: #linux #utils
—
🟢 Подпишись: @bashdaysbash <(curl -s https://raw.githubusercontent.com/wick3dr0se/snake/main/snake)Игруха создана на чистом bash v5.1+. Плюсом можно потренить навигацию в VIM используя hjkl, ну и стрелочки никто не отменял. Отлично отключает голову, после восьми часового дебагинга. Денёк сегодня, конечно вообще атас. А кто еще какие игрули консольные знает без геморной установки? Кидай в комменты, заценим! tags: #bash #games — 🟢 Подпишись: @bashdays
اکنون در دسترس! پژوهش تلگرام ۲۰۲۵ — مهمترین بینشهای سال 
