Вечерок вчера весёлый получился, в плане отвала RU зоны. Ну да ладно, само все починилось, меня даже ночью не дернули, удалось поспать 5 часов.
Сегодня продолжим тему с нулями, но на этот раз обсудим такое понятие как — Разрежённый файл. Или как их звать-величать «Дырявые файлы».
Ты всяко в курсе, когда в виртуалке создаешь жесткий диск размером 500 гигабайт, то физический размер этого диска на хостовой машине будет около 1-2х гигабайта, само собой с установленной Linux системой.
Ты суслика видишь? Нет. И я не вижу, а он есть.
А где же все остальное место? А нигде, есть только информация, которая хранится об этом размере в метаданных файловой системы. И по мере необходимости это физическое место на диске выделяется и используется. Сам же файл представляет собой последовательность тех самых нулевых байт (magic zero).
Если уж совсем просто: Дырявые файлы - это файлы, которые выделяют пространство на диске только для хранения реально используемых данных.
Чтобы стало понятнее, давай тыкать палкой.
Создаем дырявый файл:
Создался файл bashdays размером 4 мегабайта. Да, не 100, а именно 4ре. Открываем в режиме просмотра и видим в нём нули. По факту его можно растянуть как гандон до 100 мегабайт. Размер будет увеличиваться, по мере заполнения этого файла данными, но не сможет превысить 100 мегабайт.
Как альтернатива, ты можешь создать такой файл так:
Окей, файл есть и что дальше? А дальше этот файл нужно чем-то заполнить.
Дырявые файлы обычно применяются для:
- экономии места на диске
- виртуализации
- резервных копий
- баз данных
- файловых систем
Очевидно это связанно с оптимизацией, ну и чтобы твой любимый жесткий диск не насиловать физическими записанными нулями.
Из всего перечисленного, я использую такие разрежённые файлы как файловую систему для экспериментов. Создаю файл, а затем просто монтирую ее как файловую систему, провожу опыты, издеваюсь, растягиваю, а затем удаляю.
Чтобы примонтировать такой файл как ФС, делаешь так:
Первая накатит туда ext4, а вторая смонтирует в /mnt. А еще в такой дырявый файл можно установить операционную систему и даже загрузится в неё. Короче тут кейсов много всяких, все зависит от твоих задач и предпочтений. Но тема да, прикольная. Всякие virtualbox и т.п. это активно используют.
Теперь про хостеров
Хостеры и не только, часто используют данную фичу, чтобы раскатывать тебе VPS за несколько секунд, единицы ставят ОС с нуля и выдают «честные» гигабайты на жестком диске.
Логично, что хостеры НЕ будут резервировать тебе 100 гигабайт места на диске. Возможно ты ими пользоваться вообще не будешь. Чо оно будет просто так болтаться? А денежку конечно же будут брать именно за 100 гигабайт. По факту ты платишь за дырявый файл, который возможно никогда и не заполнится до конца.
Например, если в яндекс облаке выключить все виртуалки, денежка все равно будет капать за дисковое пространство. Улавливаешь?
Как это работает: есть эталонные образы дырявого файла, разного размера. Ты выбираешь у хостера тариф например 2/2/30, затем берется заранее подготовленный дырявый файл с нужным размером диска и отдается тебе.
Ты видишь (через df -h) что на винте 30 гигабайт, радуешься, но на самом деле это место еще никак не распределено. Используется только 4-5 гигабайта. По мере заполнения данными, заполняется и твой дырявый файл, который тебе продали за 10$ в месяц. Получается так, что в большинстве случаев у тебя в запасе всегда болтается процентов 20 от объема диска, которые ты не используешь, но исправно оплачиваешь.
Такая вот замечательная бизнес модель, такие вот замечательные разрежённые файлы.
Казино всегда в плюсе и ставит на
UPD: Коллеги отметили, что лучше так:
tags: #linux
—
💩 @bashdays
Сегодня продолжим тему с нулями, но на этот раз обсудим такое понятие как — Разрежённый файл. Или как их звать-величать «Дырявые файлы».
Ты всяко в курсе, когда в виртуалке создаешь жесткий диск размером 500 гигабайт, то физический размер этого диска на хостовой машине будет около 1-2х гигабайта, само собой с установленной Linux системой.
Ты суслика видишь? Нет. И я не вижу, а он есть.
А где же все остальное место? А нигде, есть только информация, которая хранится об этом размере в метаданных файловой системы. И по мере необходимости это физическое место на диске выделяется и используется. Сам же файл представляет собой последовательность тех самых нулевых байт (magic zero).
Короче сохраняется только местоположение и количество последовательности нулевых байт. А при чтении такого файла, файловая система генерирует нужное количество нулевых байт.
Если уж совсем просто: Дырявые файлы - это файлы, которые выделяют пространство на диске только для хранения реально используемых данных.
Чтобы стало понятнее, давай тыкать палкой.
Создаем дырявый файл:
dd if=/dev/zero of=./bashdays bs=1 count=0 seek=100M
Создался файл bashdays размером 4 мегабайта. Да, не 100, а именно 4ре. Открываем в режиме просмотра и видим в нём нули. По факту его можно растянуть как гандон до 100 мегабайт. Размер будет увеличиваться, по мере заполнения этого файла данными, но не сможет превысить 100 мегабайт.
Как альтернатива, ты можешь создать такой файл так:
truncate -s100M ./bashdays
Окей, файл есть и что дальше? А дальше этот файл нужно чем-то заполнить.
Дырявые файлы обычно применяются для:
- экономии места на диске
- виртуализации
- резервных копий
- баз данных
- файловых систем
Очевидно это связанно с оптимизацией, ну и чтобы твой любимый жесткий диск не насиловать физическими записанными нулями.
Из всего перечисленного, я использую такие разрежённые файлы как файловую систему для экспериментов. Создаю файл, а затем просто монтирую ее как файловую систему, провожу опыты, издеваюсь, растягиваю, а затем удаляю.
Чтобы примонтировать такой файл как ФС, делаешь так:
mkfs.ext4 ./bashdays
mount ./bashdays /mnt
Первая накатит туда ext4, а вторая смонтирует в /mnt. А еще в такой дырявый файл можно установить операционную систему и даже загрузится в неё. Короче тут кейсов много всяких, все зависит от твоих задач и предпочтений. Но тема да, прикольная. Всякие virtualbox и т.п. это активно используют.
Теперь про хостеров
Хостеры и не только, часто используют данную фичу, чтобы раскатывать тебе VPS за несколько секунд, единицы ставят ОС с нуля и выдают «честные» гигабайты на жестком диске.
Логично, что хостеры НЕ будут резервировать тебе 100 гигабайт места на диске. Возможно ты ими пользоваться вообще не будешь. Чо оно будет просто так болтаться? А денежку конечно же будут брать именно за 100 гигабайт. По факту ты платишь за дырявый файл, который возможно никогда и не заполнится до конца.
Например, если в яндекс облаке выключить все виртуалки, денежка все равно будет капать за дисковое пространство. Улавливаешь?
Как это работает: есть эталонные образы дырявого файла, разного размера. Ты выбираешь у хостера тариф например 2/2/30, затем берется заранее подготовленный дырявый файл с нужным размером диска и отдается тебе.
Ты видишь (через df -h) что на винте 30 гигабайт, радуешься, но на самом деле это место еще никак не распределено. Используется только 4-5 гигабайта. По мере заполнения данными, заполняется и твой дырявый файл, который тебе продали за 10$ в месяц. Получается так, что в большинстве случаев у тебя в запасе всегда болтается процентов 20 от объема диска, которые ты не используешь, но исправно оплачиваешь.
Такая вот замечательная бизнес модель, такие вот замечательные разрежённые файлы.
Казино всегда в плюсе и ставит на
/dev/zero. Делайте ваши ставки, дамы и господа!UPD: Коллеги отметили, что лучше так:
fallocate -l 1G --punch-hole sparsefiletags: #linux
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. Сегодня рассмотрим ситуацию… короче предыстория:
Я саппортю ебически большой проект на php, в нем миллион файлов и папок. Есть некий файл с выгрузкой для бухов, который генерится по крону и куда-то складывается.
Знаем точно, что он складывается как раз в тот ебический проект с миллионом файлов. Но не знаем как называется сам файл и где конкретно он сохраняется.
Задача: найти местоположение файла с выгрузкой, узнать как называется этот файл и убедиться что файл вообще пытается создаться после запуска крона.
Естественно нет документации, нет контактов с разработчиками, нет НИЧЕГО у кого можно что-то узнать. Это обычная практика в айти, вот вам проект, ебитесь сами. В общем приступаем к поискам.
Лазить по каждой папке я не буду, тем более изучать конфиги и насиловать find. Я стараюсь не делать, то что мне не нравится. А вот накидать bash скрипт, это прям хлебом не корми, дай заговнокодить.
Ладно, давай к делу. Отбрасываем все стандартные методы и скрам практики, ставим пакет inotify-tools и накидываем скрипт.
Да, мысль ты правильно понял, я сделал что-то на подобии снифера, который будет рекурсивно перехватывать события создания файла/папки и выводить нужную мне информацию на экран. Проще говоря, как только в папках с проектом что-то создастся, оно закричит и покажет мне, то что создалось.
Что круто, мониторинг осуществляется рекурсивно, достаточно указать одну папку, а все подпапки автоматически попадут под колпак.
Запускаем скрипт. В соседней вкладке запускаем команду, которая генерит этот вонючий файл с выгрузкой и ждем. Совсем скоро на экране я получаю такую запись:
Опа, задача решена, путь до файла определен, как и его имя. Пишем комментарий к задаче, передвигаем её в Done и трекаем 8 часов потраченного времени.
Вообще игрушка inotify-tools достаточно мощная, можно мониторить не только создание файлов, но и кучу еще всего. При должном подходе, можно свой собственный мониторинг собрать, выгружать в prometheus и соплю в grafana рисовать.
Про все ее возможности писать не буду, всё прекрасно гуглится, да и man у нее хороший. Я лишь показал, как применил эту штуку на реальной задаче и сэкономил кучу времени. А дальше тебе карты в руки.
А еще есть клевая штука incrond, с помощью неё можно запустить какую-нибудь утилитку, если в каком-то каталоге появился новый файл или произойдет другое событие.
Например, можно дернуть обработчик этого файла, сделать резервную копию, или тегнуть в телеграм. Вариантов масса.
Я однажды видел подобную поделку на каком-то сервере у хостера, где админы сильно упарывались с безопасностью, отслеживали вообще любые изменения в рабочих каталогах и слали алерты на любой чих. С фейками для отнятия почт так боролись. Ну не знаю… по моему совсем уж избыточно.
Ладно, не болей, хороших тебе предстоящих выходных и береги себя!
tags: #linux #bash #debug
—
💩 @bashdays
Я саппортю ебически большой проект на php, в нем миллион файлов и папок. Есть некий файл с выгрузкой для бухов, который генерится по крону и куда-то складывается.
Знаем точно, что он складывается как раз в тот ебический проект с миллионом файлов. Но не знаем как называется сам файл и где конкретно он сохраняется.
Задача: найти местоположение файла с выгрузкой, узнать как называется этот файл и убедиться что файл вообще пытается создаться после запуска крона.
Естественно нет документации, нет контактов с разработчиками, нет НИЧЕГО у кого можно что-то узнать. Это обычная практика в айти, вот вам проект, ебитесь сами. В общем приступаем к поискам.
Лазить по каждой папке я не буду, тем более изучать конфиги и насиловать find. Я стараюсь не делать, то что мне не нравится. А вот накидать bash скрипт, это прям хлебом не корми, дай заговнокодить.
Ладно, давай к делу. Отбрасываем все стандартные методы и скрам практики, ставим пакет inotify-tools и накидываем скрипт.
inotifywait -r -m -e create /var/www/bashdays | while read line; do
echo "Created: $line"
done
inotify-tools - это набор утилит командной строки для мониторинга файловой системы в реальном времени на основе inotify, механизма ядра Linux, который позволяет приложениям реагировать на изменения файлов, каталогов или метаданных в файловой системе.
Да, мысль ты правильно понял, я сделал что-то на подобии снифера, который будет рекурсивно перехватывать события создания файла/папки и выводить нужную мне информацию на экран. Проще говоря, как только в папках с проектом что-то создастся, оно закричит и покажет мне, то что создалось.
Что круто, мониторинг осуществляется рекурсивно, достаточно указать одну папку, а все подпапки автоматически попадут под колпак.
Запускаем скрипт. В соседней вкладке запускаем команду, которая генерит этот вонючий файл с выгрузкой и ждем. Совсем скоро на экране я получаю такую запись:
Created: /var/www/bashdays/core/modx/var/tmp CREATE 1C_b2b_prices.csv
Опа, задача решена, путь до файла определен, как и его имя. Пишем комментарий к задаче, передвигаем её в Done и трекаем 8 часов потраченного времени.
Вообще игрушка inotify-tools достаточно мощная, можно мониторить не только создание файлов, но и кучу еще всего. При должном подходе, можно свой собственный мониторинг собрать, выгружать в prometheus и соплю в grafana рисовать.
Про все ее возможности писать не буду, всё прекрасно гуглится, да и man у нее хороший. Я лишь показал, как применил эту штуку на реальной задаче и сэкономил кучу времени. А дальше тебе карты в руки.
А еще есть клевая штука incrond, с помощью неё можно запустить какую-нибудь утилитку, если в каком-то каталоге появился новый файл или произойдет другое событие.
Например, можно дернуть обработчик этого файла, сделать резервную копию, или тегнуть в телеграм. Вариантов масса.
incrond (inotify cron daemon) - это демон для системы Linux, который обеспечивает возможность запуска задач (команд или скриптов) в ответ на события файловой системы, используя механизм inotify. Он подобен стандартному демону cron, но вместо использования времени он реагирует на изменения файлов и каталогов.
Я однажды видел подобную поделку на каком-то сервере у хостера, где админы сильно упарывались с безопасностью, отслеживали вообще любые изменения в рабочих каталогах и слали алерты на любой чих. С фейками для отнятия почт так боролись. Ну не знаю… по моему совсем уж избыточно.
Ладно, не болей, хороших тебе предстоящих выходных и береги себя!
tags: #linux #bash #debug
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет, тут на днях в нашем чатике пролетала софтина которая делает этакий ханипот на 22 порту. И все кто ломится по ssh просто вязнут в этом болоте.
Но сегодня не про это. Сегодня про злоумышленников, которые методом тыка пытаются найти и украсть бэкап. Зачастую бэкапы хранятся в папках проекта, либо вообще в корне пылятся годами.
Как это бывает: переезжаешь на другой сервер, перетащил проект в zip архиве, распаковал, а сам zip не удалил. И теперь любой кто узнает имя архива, сможет его скачать, со всеми паролями и явками. Ну либо как в распространенных движках, есть всем известная папка, где могут находиться эти бэкапы.
Вообще руками имена файлов никто не подбирает, для этого есть специальный софт, который просто брутфорсит по словарю всевозможные локейшены и если получает статус 200, то скачивает всё, что плохо лежит.
Как с этим бороться? Ничего не бэкапить в папки проекта, удалять промежуточные файлы если что-то переносил, настраивать политики nginx чтобы 403 отдавал и т.п.
Но есть способ сделать простой ханипот. Злоумышленник якобы находит бэкап, получает статус 200 и начинает его скачивать. Естественно это не бэкап, а специально подготовленный файл большого размера. Причем такой файл отдается специально с пониженной скоростью и санкциями.
Повторюсь, маловероятно, что злоумышленник это человек. В основном сбором бигдаты занимается автоматизированный софт. Устанавливается на уже скомпрометированные машины, с которых осуществляется сканирование каталогов на наличие плохо лежащих файлов.
Давай настроем такой ханипот на примере nginx
Добавляем в nginx конфиги:
Указываем список на что будем триггериться. Ограничиваем скорость, ограничиваем число потоков, запрещаем докачку.
Создаем файл затычку размером 1гиг, этого вполне хватит. Но можешь конечно сделать и больше.
Вот и всё. Теперь если кто-то попытается вытянуть у тебя бэкап к примеру mysql.tar.gz, то будет страдать. Идея думаю тебе понятна, вокруг нее можно городить вообще сумасшедшие схемы. Но опять же это всего лишь концепт, из которого можно сделать что-то большое и нужное.
Я одно время применял этот способ в случае, когда коллеги просят выдать бэкап базы. Обычно я даю прямую ссылку на скачивание. Но бывают ситуации, когда разработчики ведут себя как зажравшиеся ЧСВешные мудаки. И на такой случай у меня для них была специальная ссылка для скачивания бэкапа.
Ничего особенного, но с сюрпризом, бэкап никогда не скачается. Тащить 10 гигабайт со скоростью dial-up ну такое себе. А когда те начинали орать как свинья из avp, мой ответ был простой - проблема на твоей стороне. У меня видишь все работает и показываю скриншот с нормальной скоростью.
Но таким быть фу, заподлостроением пусть школьники занимаются. Мы с вами взрослые и ответственные ребята. Правда? )
Ладно, рад всех видеть. Всем хорошей рабочей недели!
tags: #nginx #networks
—
💩 @bashdays
Но сегодня не про это. Сегодня про злоумышленников, которые методом тыка пытаются найти и украсть бэкап. Зачастую бэкапы хранятся в папках проекта, либо вообще в корне пылятся годами.
Как это бывает: переезжаешь на другой сервер, перетащил проект в zip архиве, распаковал, а сам zip не удалил. И теперь любой кто узнает имя архива, сможет его скачать, со всеми паролями и явками. Ну либо как в распространенных движках, есть всем известная папка, где могут находиться эти бэкапы.
Вообще руками имена файлов никто не подбирает, для этого есть специальный софт, который просто брутфорсит по словарю всевозможные локейшены и если получает статус 200, то скачивает всё, что плохо лежит.
Как с этим бороться? Ничего не бэкапить в папки проекта, удалять промежуточные файлы если что-то переносил, настраивать политики nginx чтобы 403 отдавал и т.п.
Но есть способ сделать простой ханипот. Злоумышленник якобы находит бэкап, получает статус 200 и начинает его скачивать. Естественно это не бэкап, а специально подготовленный файл большого размера. Причем такой файл отдается специально с пониженной скоростью и санкциями.
Повторюсь, маловероятно, что злоумышленник это человек. В основном сбором бигдаты занимается автоматизированный софт. Устанавливается на уже скомпрометированные машины, с которых осуществляется сканирование каталогов на наличие плохо лежащих файлов.
Давай настроем такой ханипот на примере nginx
Добавляем в nginx конфиги:
location ~* "^/(new|old|bkup|tmp|temp|upload|ftp|sql|file|www|drupal|joomla|wordpress|x|user|admin|a|b|r|rezerv|arch|arx|111|archive|auth|backup|clients|com|dat|dump|engine|files|home|html|index|master|media|my|mysql|old|site|sql|website|wordpress)\.tar.gz$" {
access_log /var/log/nginx/honeypot.log;
default_type application/zip;
root /var/www/bashdayz/files/backup;
rewrite ^(.*)$ /backup break;
max_ranges 0;
limit_rate 4k;
limit_conn addr 1;
}
# а это в секцию http
limit_conn_zone $binary_remote_addr zone=addr:10m;Указываем список на что будем триггериться. Ограничиваем скорость, ограничиваем число потоков, запрещаем докачку.
Создаем файл затычку размером 1гиг, этого вполне хватит. Но можешь конечно сделать и больше.
dd if=/dev/zero of=/var/www/bashdayz/files/backup bs=1G count=1
Вот и всё. Теперь если кто-то попытается вытянуть у тебя бэкап к примеру mysql.tar.gz, то будет страдать. Идея думаю тебе понятна, вокруг нее можно городить вообще сумасшедшие схемы. Но опять же это всего лишь концепт, из которого можно сделать что-то большое и нужное.
Я одно время применял этот способ в случае, когда коллеги просят выдать бэкап базы. Обычно я даю прямую ссылку на скачивание. Но бывают ситуации, когда разработчики ведут себя как зажравшиеся ЧСВешные мудаки. И на такой случай у меня для них была специальная ссылка для скачивания бэкапа.
Ничего особенного, но с сюрпризом, бэкап никогда не скачается. Тащить 10 гигабайт со скоростью dial-up ну такое себе. А когда те начинали орать как свинья из avp, мой ответ был простой - проблема на твоей стороне. У меня видишь все работает и показываю скриншот с нормальной скоростью.
Но таким быть фу, заподлостроением пусть школьники занимаются. Мы с вами взрослые и ответственные ребята. Правда? )
Ладно, рад всех видеть. Всем хорошей рабочей недели!
tags: #nginx #networks
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. Разгребал тут мамонтовский сервер и нашел исходник с таким артом.
Думаешь это всего лишь картинка из архивов старого фидошника? Нифига, это Acme EyeDrops. Visual Programming in Perl.
В арте зашит вполне рабочий perl код. Если сохранить эту картинку в файл и затем выполнить его через интерпретатор, ты получишь секретное сообщение.
Сам код этого арта, сгенерирован с помощью модуля Acme::EyeDrops. Кстати на странице модуля много примеров с такими картинками (верблюды, морды, заборы, снежинки), каждая выполняет какое-то своё действие + есть примеры генераторов.
А вот эта команда, превратит файл обратно в исходник. То есть выведет исходный код вместо того, чтобы его выполнить.
Опять же не знаю, что ты будешь делать с этой информацией. Говорят что perl давно мёртв, но в современных скриптах и решениях люди любят его применять. ХЗ почему, наверное потому что perl есть везде. Хотя сейчас глянул, последний релиз был 2024-01-20, версия 5.39.7 (devel), капец, оно живое.
Первое и последнее, что я делал на perl, это был вывод «Hello World», на этом моё знакомство с ним закончилось. Даже на СИ порог вхождения мне легче показался, хотя по СИ у меня была книга, а по perl только какие-то огрызки из фидонета.
Ладно. Побежали дальше мир спасать от криворучек. Увидимся!
А ты применяешь perl в современном мире? Если да, поделись пожалуйста в комментариях, как и где. Очень интересно.
tags: #linux
—
💩 @bashdays
Думаешь это всего лишь картинка из архивов старого фидошника? Нифига, это Acme EyeDrops. Visual Programming in Perl.
В арте зашит вполне рабочий perl код. Если сохранить эту картинку в файл и затем выполнить его через интерпретатор, ты получишь секретное сообщение.
perl ./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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. Немного терминальной развлекухи :
А тут и тут можешь посмотреть исходники этих проектов. Там еще много подобных curl запросов можно понаделать с котиками, пончиками, бэтменами. А еще можно поставить себе на сервер и устраивать консольные вечеринки.
tags: #linux
—
💩 @bashdays
curl ascii.live/parrot
curl ascii.live/forrest
curl ascii.live/can-you-hear-me
curl parrot.live
А тут и тут можешь посмотреть исходники этих проектов. Там еще много подобных curl запросов можно понаделать с котиками, пончиками, бэтменами. А еще можно поставить себе на сервер и устраивать консольные вечеринки.
tags: #linux
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Вот и до наших краёв донеслась снежная стихия, какая-то «Ольга». Ну хоть не «Арбузова». С названием переменных явно не заморачивались. Ну да ладно, сегодня тоже будет про переменные в Bash.
Не все понимают чем отличается определение переменной в Bash c export и без. Сейчас расскажу, чтобы ты больше не сомневался.
Есть 2 распространенных способа объявить переменную:
Что тут не так? А тут всё так. Только в первом случае я использую export, чтобы сделать переменную доступной для подпроцессов.
То есть, переменная заданная через export будет доступна для любого процесса, который будет запущен в рамках процесса оболочки.
А если задать переменную БЕЗ export, то переменная будет доступна только в самой оболочке и недоступна для других процессов.
Скучная теория, давай на практике:
Запускаем, смотрим, ага переменная задается без экспорта и выводится на экран. Идем дальше и запускаем это:
1. Экспортируем var1
3. Объявляем вторую переменную var2
4. Запускаем bash в bash (подпроцесс)
5. Получаем значение переменной var1
6. Получаем значение переменной var2
В пятом пункте переменная var1 вывелась на экран даже после того, как я запустил еще одну оболочку bash внутри оболочки bash. Переменная стала доступна для подпроцессов.
А вот var2 которую я объявил во втором пункте, куда-то делась. На самом деле она никуда не делась, просто подпроцесс её не видит. Стоит мне сейчас сделать так:
Я получаю значение переменной var2. Если уж совсем грубо говоря, export делает переменную глобальной на уровне подпроцессов.
Ну и частые ошибки, когда пытаешься экспортировать массив:
С виду все в порядке и вроде логично, ошибок нет. НО в подпроцессах если попытаться вывести содержимое этого массива, ты получишь пустоту. И люди на это убивают несколько часов, чтобы понять в чем причина.
Но где наша не пропадала, если уж совсем хочется, то можно преобразовать массив в строку и затем экспортировать эту строку. Делается это так:
Значок собаки = все элементы массива, его можно справедливо заменить на 1, 2, 3 и т.п. А printf используются для форматирования массива в строку, разделяя элементы символом «|».
Теперь массив экспортирован и будет доступен в подпроцессах.
Вообще холиваров на тему экспортирования переменных очень много, каждый топит за своё, но тут уже хозяин барин. Как говорится под каждую задачу своё решение, где-то необходим export, где-то он и не нужен.
Ладно чо, хороших тебе предстоящих выходных и береги себя!
tags: #linux #bash
—
💩 @bashdays
Не все понимают чем отличается определение переменной в Bash c export и без. Сейчас расскажу, чтобы ты больше не сомневался.
Есть 2 распространенных способа объявить переменную:
export 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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. Странный факт - всю неделю с нетерпением ждешь выходных, а когда дождался, щелчок пальцами и снова наступил понедельник. А когда отдыхать-то? В рабочие дни разгребаешь рабочую рутину, в выходные разгребаешь домашнюю рутину.
Вот такой вот замкнутый круг получается. С другой стороны ты всегда находишься при деле, в потоке. Меньше времени остается на нытье и на самокопание. А это уже большой плюс. Окей, поныли и хватит, летсгоу!
Ты тёртый калач и всяко знаешь про специальные переменные со знаком $. Держу пари, знаешь только про базовые и часто используемые, типа $? и $1.
Сегодня рассмотрим еще парочку $@ и $*. Эти специальные переменные по сути выполняют одну и туже задачу: выводят список всех аргументов переданных скрипту. Но естественно есть различия в их использовании.
Голову не грей, теория всегда душная, сейчас на примерах рассмотрим и всё у тебя в голове сложится как надо.
Начнем с $@, накидываем скрипт:
Запускаем так:
На экран выводится:
Видим что каждый аргумент у нас вывелся отдельным словом. Даже несмотря на то, что последний аргумент у нас заключен в кавычки и является строкой с пробелами. В этом и особенность. Он наплевал на кавычки и разбил строку на дополнительные аргументы.
А теперь немного переделаем скрипт (добавим кавычки) и запустим с теме же аргументами:
В этот раз на экран выведется такое:
Вот! Тут мы получили уже более ожидаемый результат. Массив: {$1, $2, $3}
Теперь про $*
После запуска с такими же аргументами:
Получаем то же самое, что и с $@ без кавычек. Шило на мыло. А вот если заключить $* в кавычки:
На экране мы получим:
Вот и отличие. Оно небольшое, оно практически ненужное. Но в этом и кроются детали. Все аргументы объединились в одну строку, отделенным символом пробела, заданным в IFS (пробел по умолчанию).
Если это не знать, то легко можно наступить на грабли.
Вот пример как заменить IFS разделитель (пробел на запятую):
Вывод будет таким:
Как «эффект бабочки», забыл поставить кавычки, сломал в будущем логику. Поставил кавычки, сломал другую логику. Куда не ткни, вечно какие-то костыли.
Я конечно сомневаюсь, что ты пользуешься таким высокоуровневым программированием (я не пользуюсь), но порой бывает натыкаешься на чужие скрипты с подобной логикой. И тогда важно не попасть в просак.
А что такое просак, хорошо объясняется в фильме «Жмурки».
Ладно, не смею тебя больше отвлекать. Увидимся. Хорошего понедельника!
tags: #bash #linux
—
💩 @bashdays
Вот такой вот замкнутый круг получается. С другой стороны ты всегда находишься при деле, в потоке. Меньше времени остается на нытье и на самокопание. А это уже большой плюс. Окей, поныли и хватит, летсгоу!
Ты тёртый калач и всяко знаешь про специальные переменные со знаком $. Держу пари, знаешь только про базовые и часто используемые, типа $? и $1.
Сегодня рассмотрим еще парочку $@ и $*. Эти специальные переменные по сути выполняют одну и туже задачу: выводят список всех аргументов переданных скрипту. Но естественно есть различия в их использовании.
Голову не грей, теория всегда душная, сейчас на примерах рассмотрим и всё у тебя в голове сложится как надо.
Начнем с $@, накидываем скрипт:
echo "Изучаем \$@"
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 "Изучаем \$*"
IFS=","
for arg in "$*"; do
echo "Аргументы: $arg"
done
Вывод будет таким:
Изучаем $*
Аргументы: 1,2,3 with spaces
Как «эффект бабочки», забыл поставить кавычки, сломал в будущем логику. Поставил кавычки, сломал другую логику. Куда не ткни, вечно какие-то костыли.
Я конечно сомневаюсь, что ты пользуешься таким высокоуровневым программированием (я не пользуюсь), но порой бывает натыкаешься на чужие скрипты с подобной логикой. И тогда важно не попасть в просак.
А что такое просак, хорошо объясняется в фильме «Жмурки».
Ладно, не смею тебя больше отвлекать. Увидимся. Хорошего понедельника!
tags: #bash #linux
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. В прошлом посте про мы с тобой познакомились с $@ и $*. Сегодня разберем остатки этих прекрасных баксовых штуковин.
Теперь на примерах
Позиционные параметры
Выведет:
Количество переданных аргументов
Выведет:
Текущие флаги оболочки
Выведет:
Про эти флаги можешь почитать в этом посте.
Идентификатор процесса PID
Выведет:
Последний аргумент предыдущей команды
Выведет:
Разделение слов во входящих данных
По умолчанию у $IFS это пробел, но можно заменить на что-то другое. Тут указываем, что разделитель у нас будет «запятая».
Вывод будет таким:
Код возврата последней выполненной команды
Выведется:
PID последнего запущенного в фоновом режиме процесса
Выведет:
Имя исполняемого файла
Выведет:
А если выполнить из скрипта, то оно покажет его имя.
Вроде всё упомянул, если что-то упустил, добавляйте в комментарии.
tags: #bash
—
💩 @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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Во времена динозавров, мне удалось поработать в профильных компаниях, которые занимаются информационной безопасностью и тестированием инфраструктуры клиентов на проникновение.
Однажды обратился клиент, которому злые лоси вынесли инфраструктуру. И когда мы полезли разбирать инцидент, на серверах отсутствовали логи, их просто не было. Их не удалили, их отключили. Тогда еще никто не парился со всякими ELK, да и может таких решений и не существовало вовсе.
В общем тут мои глаза и раскрылись, а зачем отключают логи? Правильно, чтобы что-то скрыть. Вектор атаки простой - заимел рута, отключил логи, зачистил то что было и делай свои грязные делишки.
И мне стало интересно, а как это провернуть? А если появился интерес, надо тыкать палкой.
Сейчас будем решать эту задачу. Как именно, понятия не имею. Будем экспериментировать.
Отключаем логирование
Цель - папка var/log всегда должна быть пустая и чтобы Linux машина не встала раком. Дополнительно обуздаем journald и history от bash.
Первое, что я проверил, удалил все и запретил вообще любую запись в var/log через chmod 000, но после перезагрузки сервера, всё вернулось на свои места. Права на папку стали 755. Однако…
Теперь попробуем установить на папку специальный атрибут:
Перезагружаемся. Хе! Сработало. В папке пустота! На этом можно двигать задачу в done.
После установки этого атрибута, у тебя начнет сыпать ошибками apt, но пакеты оно будет ставить, так что имей это ввиду. Это фиксится добавлением строчки Dir::Log "/путь"; в apt.conf.d.
НО, не все так просто. Если ввести:
Мы снова видим логи. Но теперь уже на экране. А раз они на экране, значит они где-то лежат и на диске. Давай искать.
Зачищаем этот системный журнал:
Ага, на экране видим путь, то что оно зачистило, это /run/log/journal/.
Зачистка это хорошо, но логи все равно будут писаться. Надо отключить эту шляпу на глобальном уровне.
Открываем файл /etc/systemd/journald.conf и добавляем:
Перезагружаем службу и удаляем остатки:
Теперь если ввести:
Получим такое сообщение: No journal files were found. Прекрасно!
Так, если в системе установлен rsyslog или подобное, то дизейблим и его:
Основное сделали. Осталось логирование в .bash_history. Зачищаем и накидываем атрибут на запрет записи:
Либо добавляем в .bashrc строчку: HISTSIZE=0, с ней будет нативнее и без костылей с атрибутом.
Делаем финальную перезагрузку. Ну вот и всё, задача выполнена. Система зачищена от логирования. По любому я что-то упустил, поэтому жду с нетерпением твои комментарии.
Вообще все эти способы очень грубые и топорные. Было бы намного изящнее сделать решение, которое удаляет из логов только то, что нужно удалить. Логирование работает в штатном режиме, но критичные данные в логи не попадают.
Всех обнимаю, хорошего дня!
Прошу отметить, что предоставленная здесь информация предназначена исключительно для образовательных и информационных целей. Я не призываю и не одобряю незаконные действия, и использование этой информации для незаконных целей запрещено. Читатели должны соблюдать законы своей страны и использовать свои навыки с уважением к этическим нормам и законам.
tags: #bash #linux
—
💩 @bashdays
Однажды обратился клиент, которому злые лоси вынесли инфраструктуру. И когда мы полезли разбирать инцидент, на серверах отсутствовали логи, их просто не было. Их не удалили, их отключили. Тогда еще никто не парился со всякими ELK, да и может таких решений и не существовало вовсе.
В общем тут мои глаза и раскрылись, а зачем отключают логи? Правильно, чтобы что-то скрыть. Вектор атаки простой - заимел рута, отключил логи, зачистил то что было и делай свои грязные делишки.
И мне стало интересно, а как это провернуть? А если появился интерес, надо тыкать палкой.
Сейчас будем решать эту задачу. Как именно, понятия не имею. Будем экспериментировать.
Отключаем логирование
Цель - папка var/log всегда должна быть пустая и чтобы Linux машина не встала раком. Дополнительно обуздаем journald и history от bash.
Первое, что я проверил, удалил все и запретил вообще любую запись в var/log через chmod 000, но после перезагрузки сервера, всё вернулось на свои места. Права на папку стали 755. Однако…
Теперь попробуем установить на папку специальный атрибут:
sudo 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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Занятный факап приключился у клиента. На скрине думаю понятно, что произошло. Добро пожаловать в Linux!
Эту задачу мне удалось решить за несколько минут, не имея бэкапов я восстановил ему этот удаленный файл. Как раз способом из этого поста.
Если кратко, я глянул в логи /var/log/cron, взял рандомную таску которая выполнялась ранее и вбил:
Соответственно console import:reports это часть таски, которая когда-то выполнялась. Команда отработала и нашла мне удаленный cron файл. Копипаста и все рады. Но тут опять же повезло, что данные не перезаписались на диске.
В любом случае можно было бы восстановить по хронологии из файла /var/log/cron, но уже с геморроем. Либо грепнуть CRON в syslog и journald.
На будущее сделал клиенту алиас:
Этакая защита от дурака и толстых пальцев. Теперь если перепутать «e» и «r», то оно выругается и запросит подтверждение:
crontab: really delete root's crontab? (y/n)
Ну и на будущее, не забывай делать бэкапы. Хотя сколько не говори, никто не делает, пока петух не клюнет. Хотя сколько меня не клевал, я все равно зачастую забиваю, видимо уверен, что смогу всё восстановить.
Бэкап крона:
Восстановить:
А вообще часто советуют использовать systemd timers вместо cron или systemd-cron, мол они гибче и безопаснее. Ну хз хз, каждый привык выбирать свои игрушки.
Всех с пятницей, хороших предстоящих выходных. Увидимся!
tags: #bash #linux
—
💩 @bashdays
Эту задачу мне удалось решить за несколько минут, не имея бэкапов я восстановил ему этот удаленный файл. Как раз способом из этого поста.
Если кратко, я глянул в логи /var/log/cron, взял рандомную таску которая выполнялась ранее и вбил:
grep -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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет. Сегодня про хитрости с пайпами и кодами завершения. Максимально упрощаю, чтобы стало понятнее.
Собираем такую команду:
Если после её выполнения сделать: echo $?, то получим статус выхода 0. То есть у нас есть статус завершения именно команды true. НО теперь узнать с каким кодом завершилась первая команда false?
А вот так, модифицируем команду:
В этом случае будет получен код завершения 1, который справедлив для команды false. Как ты можешь заметить, если в PIPESTATUS изменить [0] на [1], мы снова получим статус завершения 0 (то есть для true).
Аналогично можно манипулировать любой частью пайпа и получать нужные тебе статусы.
Есть еще много велосипедов для реализации желаемого, но этот коробочный самый распространенный и понятный. Изучай!
Больше про коды завершения, можешь найти в этом посте.
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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Тадам! Сегодня научимся определять, отправляется ли стандартный вывод скрипта в терминал либо передается с помощью pipe в другой скрипт/утилиту. Чтобы стало понятнее, нужно читать дальше.
Есть у меня некий Bash скрипт, пусть будет таким:
Если его выполнить, то на экран выведется ожидаемый результат. Но что если я хочу добавить логику в этот скрипт, которая будет детектить pipe и отдавать другой ожидаемый результат?
Давай по порядку, смотри:
Этот скрипт выведет строку Hello Bashdays Welcome, где слово Bashdays будет жирным (bold).
Дальше я хочу, чтобы при использовании pipe, слово BashDays НЕ выделялось жирным. То есть если запустить скрипт самостоятельно, без использования пайпов, то текст будет жирным. Если скрипт запущен через пайп, то текст остается нормальный.
Пригодится например, чтобы не передавать утилитам всякие ненужные управляющие символы, а на экран выводить опрятную и ухоженную девочку. Про управляющие символы можешь почитать в этом посте.
Давай мутить
В POSIX оболочках существует флаг -t, с помощью которого можно задетектить файловый дескриптор. Если файловый дескриптор fd = True, то скрипт запущен самостоятельно в терминале. Дескриптор fd может принимать значения:
Пишем скрипт с логикой:
Запускаем:
Видим сработал детект. Скрипт самостоятельно определил, как он был запущен и вывел на экран, то что требуется.
Флаг -t справедлив не только для Bash, он будет работать во всех POSIX оболочках.
Вот такие вот интересные кейсы порой приходится решать, благо всё это давно придумано за нас, главное знать про эту срань. Ну теперь и ты про неё знаешь.
Хорошего дня, увидимся!
tags: #bash
—
💩 @bashdays
Есть у меня некий Bash скрипт, пусть будет таким:
echo "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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
А спорим ты не знаешь чем отличается:
от
Держу пари многие, используют оба варианта даже не задумываясь. Порой в скриптах я могу одновременно встретить как первую, так и вторую конструкцию. И аргумент всегда один - но оно же работает, чо душнишь?
Если на чистоту, то эти два примера ничем не отличаются, логика работы будет одинакова. Выполнится какая-либо команда (command) и ее результат запишется в переменную var.
Второй вариант с надгробиями звучит как-то прикольнее, чем какие-то палки-копалки.
А в чём, тогда подвох?
Да всё просто - gravemarks (надгробия) морально устарели и в бест-практиках нынче $(command).
Ну и конечно же при использовании надгробий будет много геморроя например с вложенными командами и экранированием. Вот пример:
либо
Очевидно что первый вариант предпочтительнее, чем второй. Предпочтительнее в плане читаемости и ошибку в синтаксисе ты вряд ли допустишь. Ведь чем сложнее будет команда, тем больше этих надгробий придётся использовать. Возникает проблема вложенности.
Ну и порой использование gravemarks влияет на результаты:
Вроде две одинаковые команды, а выведут разный результат:
Обратная косая черта внутри надгробий обрабатывается неочевидным образом. Но если пропатчить таким образом вторую команду, то все становится на свои места:
В результате получим вывод на экран: $A как и в первом варианте с $().
Вот такие пироги. Так что если до сих пор используешь gravemarks, посмотри в сторону $(), оно как бы правильнее. Но опять же, со своим уставом куда-то там не ходят. Выбор каждого.
Ладно. Давай краба, увидимся!
tags: #bash
—
💩 @bashdays
var=$(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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Помнишь я писал про shellcheck, который проверяет Bash скрипты на синтаксические и подобные ошибки. Ну так вот, у интерпретатора Bash есть подобный функционал прям из коробки.
Суть - проверить скрипт на ошибки, до запуска.
Перед запуском скрипта, добавь флаг -n и оно тебе скажет, накосячил ты или нет.
Конечно коробочный вариант не такой гибкий как shellchek, но зато ничего не нужно устанавливать лишнего. Это как nginx -t перед его ребутом. Зачем ставить что-то внешнее, если это уже предусмотрено в самой программе. Ну только если расширить функционал и получить больше плюшек.
А если работаешь с гитом, можешь сделать pre-hook и гавно в репу уже не улетит (но если постараешься, то улетит):
После запуска этого хука проверяются все * .sh скрипты в папке src. Пример скрипта с ошибкой:
Хуй Хук выдаст тебе сообщение
Нечто подобное было прикручено к puppet очень давно в одной студии, был какой-то внутренний линтер на хуке гита, но хлебнул я знатно с ним. Мне пушить, а он на какие-то фантомные пробелы орет падла. Короче как только встал у руля, избавился и от этого вонючего паппета, от хуков и от команды.
Ну или как вариант можно включать set -u, например если переменную не объявил и попытался где-то её использовать.
Или так:
Включает режим noexec (режим «нет выполнения»). Когда этот режим включен, скрипт не будет выполняться, а только проверяться на синтаксические ошибки. В общем много вариантов есть, чтобы проверить не запуская.
А завтра кстати внеплановая пятница! Ждем ждем…
tags: #bash
—
💩 @bashdays
Суть - проверить скрипт на ошибки, до запуска.
Перед запуском скрипта, добавь флаг -n и оно тебе скажет, накосячил ты или нет.
Конечно коробочный вариант не такой гибкий как shellchek, но зато ничего не нужно устанавливать лишнего. Это как nginx -t перед его ребутом. Зачем ставить что-то внешнее, если это уже предусмотрено в самой программе. Ну только если расширить функционал и получить больше плюшек.
А если работаешь с гитом, можешь сделать pre-hook и гавно в репу уже не улетит (но если постараешься, то улетит):
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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
С наступающим! Давным-давно я избавился от OpenVPN и сейчас активно продолжаю использовать WireGuard. Даже порой связываю продакшен сервера wg тоннелями в разных регионах, где нет возможности это сделать из коробки. Стабильно, быстро, бесплатно.
Всё было хорошо, пока я не повзрослел и непознал джаззз установил себе Windows. Ну и естественно запихал туда официальный гуёвый клиент от wg. А что могло пойти не так? А всё!
Клиент в рандомный момент показывал средний палец и работал через хуй копыто.
Приходилось его выгружать, перезапускать службы, иногда передергивать софтверно сетевую карту, чтобы не ребутить машину. Ну такое себе удовольствие… Мне блять/блядь работать надо, а я тут утехами занимаюсь.
Так я ебался с полгода, но в этот понедельник меня это окончательно заебало! Пошел искать альтернативы, конкретно гуишного клиента. Потому что сама технология wg меня устраивает, не устраивают эти постоянные глюки.
И да, я много гуглил про эти проблемы, они имеются и фиксятся именно, так как я их и фиксил, перезагрузками, службами и другими костылями.
Короче перебрал я разные клиенты, всё шлак, ну и наткнулся на гитхабе на что-то приличное, простое и даже с фичами.
Называется эта штука TunnlTo. Работает в коллаборации с WireSock. По бенчмаркам обгоняет даже официальный WG клиент. На странице проекта там все подробно описано, можешь самостоятельно ознакомиться.
Чо понравилось:
1. Не глючит, шустрый, не виснет
2. Поддержка импорта тоннелей из официального wg клиента
3. При загрузки ОС можно автоматом подключать/не подключать VPN
4. Разделение приложений, кто идет через VPN, а кто нет
5. Ну и конечно другие фичи, какие хз, мне 4х хватает
В общем мне пока нравится. Заточено под винду, но у маководов и линукс-гиков и так всё хорошо, мой личный пруф.
💻 Страница проекта на github
Неделька выдалась продуктивная и очень нервная, в плане работы, в плане домашней рутины, в плане партнерских интеграций. Надо уже новых коллег к нам приводить, а я всё фигнёй какой-то занимаюсь. Исправлюсь! На выходные сделаем небольшую паузу, буду готовить контент план, чтобы уж совсем из пальца темы не высасывать. Да и ты отдохнешь.
Ну а тебе и твоим близким желаю хороших предстоящих выходных, берегите себя ребят. Увидимся!
tags: #utils #windows #networks
—
💩 @bashdays
Всё было хорошо, пока я не повзрослел и не
Клиент в рандомный момент показывал средний палец и работал через хуй копыто.
Приходилось его выгружать, перезапускать службы, иногда передергивать софтверно сетевую карту, чтобы не ребутить машину. Ну такое себе удовольствие… Мне блять/блядь работать надо, а я тут утехами занимаюсь.
Так я ебался с полгода, но в этот понедельник меня это окончательно заебало! Пошел искать альтернативы, конкретно гуишного клиента. Потому что сама технология wg меня устраивает, не устраивают эти постоянные глюки.
И да, я много гуглил про эти проблемы, они имеются и фиксятся именно, так как я их и фиксил, перезагрузками, службами и другими костылями.
Короче перебрал я разные клиенты, всё шлак, ну и наткнулся на гитхабе на что-то приличное, простое и даже с фичами.
Называется эта штука TunnlTo. Работает в коллаборации с WireSock. По бенчмаркам обгоняет даже официальный WG клиент. На странице проекта там все подробно описано, можешь самостоятельно ознакомиться.
Чо понравилось:
1. Не глючит, шустрый, не виснет
2. Поддержка импорта тоннелей из официального wg клиента
3. При загрузки ОС можно автоматом подключать/не подключать VPN
4. Разделение приложений, кто идет через VPN, а кто нет
5. Ну и конечно другие фичи, какие хз, мне 4х хватает
В общем мне пока нравится. Заточено под винду, но у маководов и линукс-гиков и так всё хорошо, мой личный пруф.
Неделька выдалась продуктивная и очень нервная, в плане работы, в плане домашней рутины, в плане партнерских интеграций. Надо уже новых коллег к нам приводить, а я всё фигнёй какой-то занимаюсь. Исправлюсь! На выходные сделаем небольшую паузу, буду готовить контент план, чтобы уж совсем из пальца темы не высасывать. Да и ты отдохнешь.
Ну а тебе и твоим близким желаю хороших предстоящих выходных, берегите себя ребят. Увидимся!
tags: #utils #windows #networks
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Доброе утро! Все выходные разгребал бухгалтерию, да готовил рекламную кампанию на завтра. Даже баг мимолётом поправил у клиента. После подключения ddos-guard, мониторинг сильно засопливил. Ну тут очевидно, docker контейнеры с экспортерами снимают показатели по домену, а домен-то теперь за ddos-guard, пришлось прокинуть hosts файл и ходить напрямую за метриками.
Ладно. Сегодня на повестке дня: Зачем добавлять ./ перед исполняемым файлом или скриптом.
Когда я только начал изучение linux, я столкнулся с проблемой, что не могу выполнить bash скрипт. Вроде и атрибут +x указан, но после запуска получалидинахуй:
Интернета тогда не было, максимум на что я мог рассчитывать это Фидонет. Но он мне не понадобился, методом тыка я самостоятельно смог решить задачу с запуском. Наверное знания MSDOS как-то натолкнули поставить перед скриптом символы
Но почему так? Это же не логично! Нет, всё логично. Это сделано для безопасности и от кривых рук. Например:
Работаешь ты под рутом, заходишь в домашнюю папку какого-нибудь пользователя, делаешь ls и хуяк, нет у тебя больше сервера. А что произошло?
А произошло то, что в папке пользователя лежал бинарник под названием ls. Который содержал руткит, он то и выполнился. Хотя ты ожидал что выполнится нативная утилита ls.
Вот поэтому если запускать какую-то дичь или скрипт БЕЗ
А вот когда указываешь
Аналогично можно запускать указывая полный путь к программе/скрипту:
Здесь так же PATH будет игнорироваться. Но вопрос остается: Как это может быть мерой безопасности, если эта мера обходится банальным
Нууу… это больше для предотвращения несчастных случаев. Указав явно
Как вариант, можешь добавить точку в PATH и тогда можешь запускать скрипты без
Добавляем текущий каталог в PATH. Теперь
Так, теперь про cron. Если хочешь чтобы твои скрипты гарантировано запускались в cron, всегда указывай полные пути до программ/утилит. Так как cron может смотреть в какой-то свой PATH или вообще через sh запускаться. Встречал/встречаю я такие случаи. Поэтому рекомендуется писать скрипты в таком духе:
Определяем полный путь до утилиты cat, а потом уже используем как переменные. Даже если cat не найдется через PATH, скрипт в кроне будет выполнен, так как указаны полные пути. Заметки про PATH можешь глянуть в этом посте.
Вот так. Хорошей тебе рабочей недели. Вечерком после интеграции закину еще чтива. На связи!
tags: #bash #linux
—
💩 @bashdays
Ладно. Сегодня на повестке дня: Зачем добавлять ./ перед исполняемым файлом или скриптом.
Когда я только начал изучение linux, я столкнулся с проблемой, что не могу выполнить bash скрипт. Вроде и атрибут +x указан, но после запуска получал
script.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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Сегодня на собесе задал кандидату вопрос, а расскажи чем отличается && и «;» при склейке консольных команд. В ответ получил — а мне это зачем знать? Если с && не заработало, то поменяю на «;».
Да и правда, у меня иногда такой же подход. Мне быстрее в моменте поменять одно на другое, чем в гугол лезть. Понятно дело если это выполняешь на критичной инфраструктуре, то такое уже не проканает. Нужно отдавать полный отчет своим действиям.
Давай разбираться чем отличается:
от:
&& (AND) это логический оператор, а вот «;» обычная последовательность действий.
В первом примере с &&, вторая команда запустится, только в том случае, если первая завершится успешным кодом возврата. Соответственно если первая накроется пиздой, произойдет короткое замыкание и вторая команда даже не соизволит запуститься.
А вот в случае с «;», вторая команда запустится при любых обстоятельствах, конечно если это не kernel panic и при условии если в оболочке не включен
Да, есть еще конструкция:
Тут у нас логический оператор OR (или). Вторая команда будет запущена, только если первая сыграет в ящик. В примере выше выполнится вторая команда, так как первая всегда завершается с ошибкой. Если false заменить на true, соответственно на экране будет пусто.
Иногда применяю это в скриптах, когда одна утилита не смогла справиться, подключается вторая.
База. Тут хочешь не хочешь, оно должно на подкорке быть. Интересно конечно, как людей на вайтишных курсах обучают. Человек отучился 6 месяцев, администратор Linux, диплом. А в голове обезьяна тарелками стучит, администрирует. Хотя тут всё от человека зависит, если ему это не интересно, то никакие курсы ситуацию не изменят.
Ладно, всякое бывает. Не смею больше задерживать. Спокойной ночи. Увидимся!
tags: #bash #linux
—
💩 @bashdays
Да и правда, у меня иногда такой же подход. Мне быстрее в моменте поменять одно на другое, чем в гугол лезть. Понятно дело если это выполняешь на критичной инфраструктуре, то такое уже не проканает. Нужно отдавать полный отчет своим действиям.
Давай разбираться чем отличается:
command && command
от:
command ; command
&& (AND) это логический оператор, а вот «;» обычная последовательность действий.
В первом примере с &&, вторая команда запустится, только в том случае, если первая завершится успешным кодом возврата. Соответственно если первая накроется пиздой, произойдет короткое замыкание и вторая команда даже не соизволит запуститься.
Про статусы и коды возврата можешь почитать в этом посте.
А вот в случае с «;», вторая команда запустится при любых обстоятельствах, конечно если это не kernel panic и при условии если в оболочке не включен
set -e (завершение при любых сбоях). Да, есть еще конструкция:
false || echo "hello bashdays"
Тут у нас логический оператор OR (или). Вторая команда будет запущена, только если первая сыграет в ящик. В примере выше выполнится вторая команда, так как первая всегда завершается с ошибкой. Если false заменить на true, соответственно на экране будет пусто.
Иногда применяю это в скриптах, когда одна утилита не смогла справиться, подключается вторая.
База. Тут хочешь не хочешь, оно должно на подкорке быть. Интересно конечно, как людей на вайтишных курсах обучают. Человек отучился 6 месяцев, администратор Linux, диплом. А в голове обезьяна тарелками стучит, администрирует. Хотя тут всё от человека зависит, если ему это не интересно, то никакие курсы ситуацию не изменят.
Ладно, всякое бывает. Не смею больше задерживать. Спокойной ночи. Увидимся!
tags: #bash #linux
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Внимание, сейчас будем упарываться. Давно не упарывались. Камон в нашу ламповую лабораторию. Сразу не сбегай, возможно узнаешь что-то новенькое или окончательно засвистит фляга.
Для начала давай создадим две подопытные папки:
Теперь скопируем в них нативную утилиту cp (copy):
Скопировали, файлы назвали lt-test.
Далее запускаем:
В параметрах указываем файл, которого нет. На экран вывелось:
А теперь запускаем:
На экран получаем:
Ну и внимательно сравниваем результаты:
Хм, всё же идентично, почему тогда результаты разные? В первом случае кусок названия запускаемого файла lt-test вообще обрезан.
В первом случае путь и прёфикс lt был обрезан, потому что запуск утилиты был выполнен из директории .libs.
Всё дело в функции
А потом эти переменные используются для вывода при ошибках или при выводе страницы хелпа.
Ну и на закуску тест:
Результат тот же:
Вся эта байда в программах нужна для корректного отображения имени, при использовании ГНУТого инструмента libtool.
Как это обрабатывается на самом деле, можешь глянуть в нативном исходнике на СИ здесь, там и про префикс lt и про .libs всё прекрасно структурировано. А здесь можешь ознакомиться с развёрнутым комментарием на эту же тему.
Короче голову не грей, на этой неделе постараюсь подобным тебя больше не развлекать. Спасибо за внимание!
tags: #linux
—
💩 @bashdays
Для начала давай создадим две подопытные папки:
mkdir /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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Привет друзья. Частый вопрос: Можно ли в bash скрипте, каким-то образом временно изменить текущий рабочий каталог для выполнения команды, но остаться в каталоге запуска?
Да всё можно, а если говорят что нельзя, то это пиздёшь. Формулировка вопроса конечно замысловатая. Сходу в голову приходит вариант использовать cd (Change Directory), тип такого:
После запуска команды, переходим в каталог
Вот наглядно что происходит:
В самой последней строке видим, что каталог изменился. А я хочу оставаться в root!
Что делать? Есть способы, которые я лично использую в своих bash скриптах. Возможно есть какие-то и другие варианты, если знаешь, пиши в комментарии, будет полезно.
Способ первый:
Добавим в скрипт exec_command такой код:
Запускаем то же самое в круглых скобках:
Что мы видим: после выполнения команды в скобках, отработала команда
Способ второй pushd/popd:
Механика аналогична первому способу. Но используются древние, инопланетные технологии pushd и popd, это коробочные аналоги cd из 80x годов. Местоположение предыдущих каталогов сохраняются в стек (массив). А потом уже из этого стека можно вытаскивать необходимое. Погугли если интересно, есть статейки.
Ну и бонусом третий способ:
Здесь используем чистый cd, но для возврата в предыдущий каталог используем cd с параметром «-».
В 99% случаев никто этим не пользуется, все привыкли в скриптах использовать cd туда-сюда с указанием полных/относительных путей. Но если посмотреть в скрипты коммерческих продуктов, в них активно используются методы с pushd и popd.
Легаси? Возможно. Тут наверное дело больше в элегантности. У каждого свои бест-практики, нет неправильных решений. Если задача приносит ожидаемый результат, значит ты всё сделал правильно.
Ладно, изучай, а я пошёл писать следующий пост про обработку сигналов. И нас тут уже 13к, идем дальше!
tags: #bash
—
💩 @bashdays
Да всё можно, а если говорят что нельзя, то это пиздёшь. Формулировка вопроса конечно замысловатая. Сходу в голову приходит вариант использовать cd (Change Directory), тип такого:
cd /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
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Время неумолимо. Кажется ещё сегодня пятница, а уже на горизонте маячит понедельник.
Привет. Сегодня про сигналы и как с ними взаимодействовать через bash скрипты. Да те самые SIGINT, SIGTERM, SIGKILL.
Вероятно ты про них слышал. Ладно, скажу больше, ты даже ими пользуешься. Банально нажимая CTRL+C, ты посылаешь сигнал SIGINT. Этот сигнал позволяет скрипту корректно завершить работу.
Если прервать скрипт по нажатию CTRL+C, то вернется код ошибки 130 (
Потому что если процесс завершается из-за полученного сигнала, он возвращает значение, которое является суммой 128 и кода сигнала. SIGINT = 2, итого получаем 128+2=130.
Для обработки сигналов, в Bash можно использовать обработчики. Спасибо, капитан!
Пример обработки сигналов:
Скрипт запустит бесконечный цикл. Если нажать CTRL+C, сработает хендлер sigint_handler и скрипт завершится с кодом 1. Видишь, мы поменяли дефолтный код 130 на 1.
А хендлер sigterm_handler сработает в том случае если скрипт убить через
Таким образом в хендлерах мы можем построить любую логику, например вывести слово «хуй». А если выпилить
Прям море возможностей открывается. В малварях частенько такой подход используют, именно с перехватом и подавлением сигналов. Но это не наша тема, просто для информации.
Да, есть еще SIGKILL, а вот его обработать не получится. Он нужен для крайних мер. Например, если из скрипта выше убрать
Но я уверен, что и SIGKILL можно перехватить, правда пока лень с этим разбираться, всяко придется в ядро лезть в strace ковыряться и возможно что-то патчить.
Существуют и другие сигналы SIGHUP / SIGUSR1 / SIGPIPE / SIGCHLD и т.п. но сути это не меняет, пост про перехват сигналов.
Обработка сигналов, это как юнит тесты, они нужны, но никто их не пишет. Но в бест-практиках очень даже рекомендуется использовать такие обработки.
В принципе на этом можно и закончить, тему разобрали, база есть, дальше уже от своих потребностей двигайся и наращивай функционал. На собесах кстати иногда спрашивают про обработку сигналов и если на этот вопрос ответить, оффер твой.
Всех с пятницей, хороших предстоящих выходных и береги себя! Увидимся!
tags: #bash #linux
—
💩 @bashdays
Привет. Сегодня про сигналы и как с ними взаимодействовать через bash скрипты. Да те самые SIGINT, SIGTERM, SIGKILL.
Вероятно ты про них слышал. Ладно, скажу больше, ты даже ими пользуешься. Банально нажимая CTRL+C, ты посылаешь сигнал SIGINT. Этот сигнал позволяет скрипту корректно завершить работу.
Если прервать скрипт по нажатию CTRL+C, то вернется код ошибки 130 (
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
—
Please open Telegram to view this post
VIEW IN TELEGRAM