Живу в лесу, поклоняюсь колесу…
ㅤ
А чё эта хуйня не работает? Яж всё правильно сделал!
С виду всё верно. Но если внимательно прочитать команду и подумать, происходит такое:
Но у
А
Поэтому правильно так:
Теперь логика не хромает:
Вроде мелочь, а на эти грабли постоянно наступают и бегут — аа, у меня принтер не печатает!!! Что я делаю не так???
🛠 #bash #badpractices #bestpractices
—
✅ @bashdays / @linuxfactory / @blog
ㅤ
А чё эта хуйня не работает? Яж всё правильно сделал!
su -c 'ls -la'
С виду всё верно. Но если внимательно прочитать команду и подумать, происходит такое:
Хочу стать root и выполнить команду ls -la.
Но у
su другое мнение:Хочу стать пользователем с именем -c
А
-c — это не имя пользователя, а опция. Поэтому правильно так:
su root -c 'ls -la'
Теперь логика не хромает:
Перейти к пользователю root и выполнить команду ls -la.
Вроде мелочь, а на эти грабли постоянно наступают и бегут — аа, у меня принтер не печатает!!! Что я делаю не так???
А чем отличается su от sudo я писал тут, почитай на досуге и обнови нейронные связи.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Сегодня будем собирать "сливки на молоке". Поговорим немного об оптимизации bash по времени исполнения.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
ㅤ
Все помнят, что есть команда time, которая с точностью до 1e-3 может подсчитать время выполнения конвейера.
Для перфекционистов, вроде меня, есть переменная
Но для ее использования есть два препятствия:
1. Она в вещественном формате, а bash не работает с вещественными числами.
2. Разделитель дробной части зависит от языковых настроек. (где-то точка, где-то запятая...)
Попробуем решить эти проблемы. Простая функция. Вычисляет разницу
Ну и теперь, вооружившись инструментом, попробуем использовать на практике:
1. Сравним время выполнения двух функций B1 - исполняется в текущей среде, B2 - исполняется в subshell
2. Сравним преобразование числа в дату с помощью
Обратите внимание - результаты отличаются на порядок.
Конечно, в реальных случаях использования, скорее всего, результаты будут отличаться меньше, но есть смысл задуматься. Особенно, если Вы будуте использовать функции во внутреннем цикле.
Первый (пустой) вызов uTIME нужен потому, что первый вызов функции всегда выполняется дольше, чем последующие. Если кого-то напрягает "0" на экране при инициализации - раскомментируйте строчку
Если нужно засечь время выполнения в контрольных точках - используйте массив:
В при каждом исполнении этого оператора в массив будет добавляться время контрольной точки. Время исполнения между точками можно будет вычислить как
Исследуйте bash, оптимизируйте, развивайтесь.
🛠 #bash
—
✅ @bashdays / @linuxfactory / @blog
ㅤ
Все помнят, что есть команда time, которая с точностью до 1e-3 может подсчитать время выполнения конвейера.
Для перфекционистов, вроде меня, есть переменная
EPOCHREALTIME, которая выдает текущее время в системе с точностью до 1e-6, т.е до микросекунды.Но для ее использования есть два препятствия:
1. Она в вещественном формате, а bash не работает с вещественными числами.
2. Разделитель дробной части зависит от языковых настроек. (где-то точка, где-то запятая...)
Попробуем решить эти проблемы. Простая функция. Вычисляет разницу
RealTime2-RealTime1 в микросекундах. Если RealTime2 не задана - принимается EPOCHREALTIME.#uTIME RealTime1 [RealTime2]
function uTIME(){
local T2=${2:-$EPOCHREALTIME}
local T1=${1:-$T2}
T2=${T2/[^0-9]/} ;T1=${T1/[^0-9]/}
# ((T2-T1)) && \
echo $((T2-T1))
}
Ну и теперь, вооружившись инструментом, попробуем использовать на практике:
1. Сравним время выполнения двух функций B1 - исполняется в текущей среде, B2 - исполняется в subshell
2. Сравним преобразование числа в дату с помощью
date и printf#!/bin/bash
function uTIME(){
local T2=${2:-$EPOCHREALTIME}
local T1=${1:-$T2}
T2=${T2/[^0-9]/} ;T1=${T1/[^0-9]/}
# ((T2-T1)) && \
echo $((T2-T1))
}
function B1(){ :;}
function B2()(:)
uTIME
echo -n 'function {} = '
T=$EPOCHREALTIME
B1
uTIME $T
echo -n 'function () = '
T=$EPOCHREALTIME
B2
uTIME $T
echo -n 'printf -v VAR "%(%Y%m%d)T" 1746024063 = '
T=$EPOCHREALTIME
printf -v VAR "%(%Y%m%d)T" 1746024063
uTIME $T
echo -n 'VAR=$(date -d@1746024063) = '
T=$EPOCHREALTIME
VAR=$(date -d@1746024063 +%Y%m%d)
uTIME $T
0
function {} = 106
function () = 2533
printf -v VAR "%(%Y%m%d)T" 1746024063 = 405
VAR=$(date -d@1746024063) = 5221
Обратите внимание - результаты отличаются на порядок.
Конечно, в реальных случаях использования, скорее всего, результаты будут отличаться меньше, но есть смысл задуматься. Особенно, если Вы будуте использовать функции во внутреннем цикле.
Первый (пустой) вызов uTIME нужен потому, что первый вызов функции всегда выполняется дольше, чем последующие. Если кого-то напрягает "0" на экране при инициализации - раскомментируйте строчку
# ((T2-T1)) && \Если нужно засечь время выполнения в контрольных точках - используйте массив:
RT+=($EPOCHREALTIME)
В при каждом исполнении этого оператора в массив будет добавляться время контрольной точки. Время исполнения между точками можно будет вычислить как
uTIME ${RT[N]} ${RT[N+1]}Исследуйте bash, оптимизируйте, развивайтесь.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Я тут недавно игрушку написал. Ну, как написал, реализовал.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
Вариант 2048, оптимизированный по занимаемому экранному пространству.
Но у нас тут люди серьезные, поэтому рассмотрим проблемы, возникающие при реализации вывода скриптов.
Простой пример:
Код просто выводит символы
ㅤ
И тогда получаем:
Убрать эхо вывода на терминал просто:
Классная команда. После ее выполнения нажатые клавиши не отображаются.
Все работает, команды выполняются, результат отображается, а набранная команда — нет. Как при наборе пароля.
Вернуть все просто:
Но в скриптах желательно обрабатывать прерывания:
Применять исключительно для причинения добра.
🛠 #bash #linux #games
—
✅ @bashdays / @linuxfactory / @blog
Вариант 2048, оптимизированный по занимаемому экранному пространству.
➡️ Ознакомиться с игрушкой можно здесь.
Но у нас тут люди серьезные, поэтому рассмотрим проблемы, возникающие при реализации вывода скриптов.
Простой пример:
for i in {1..100};do
printf "#"
sleep .3
doneКод просто выводит символы
#, примерно 3 штуки в секунду. Все работает нормально, пока пользователь не начинает что-то набирать на клавиатуре.ㅤ
И тогда получаем:
######sb#sf#bg#sfbg#wfgb#wg#fg#b###^[[A#^[[B#^[[D#^[[A#^[[B##
Убрать эхо вывода на терминал просто:
stty -echo
Классная команда. После ее выполнения нажатые клавиши не отображаются.
Все работает, команды выполняются, результат отображается, а набранная команда — нет. Как при наборе пароля.
Вернуть все просто:
stty echo
Но в скриптах желательно обрабатывать прерывания:
trap 'exit' INT HUP TERM
trap 'stty echo;tput cnorm' EXIT
# stop terminal echo
stty -echo
#hide cursor
tput civis
Применять исключительно для причинения добра.
man stty
man tput
help trap
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Давненько Bash годноты не было. Лови.
ㅤ
В этом руководстве содержится необходимая база по Bash, чтобы твой код был в кодстайле, а сами скрипты получались безопасными и предсказуемыми.
Написал её некий Дэйв Эдди в рамках серии статей — YSAP (You Suck at Programming) или по-русски — «Ебать ты лох».
Преисполниться можно тут
Ну и из терминала можно прям почитать так:
Ну а кому формат чтива не заходит, на это руководство есть видео ролики от того же иностранного гражданина.
Все ссылки на гитхабы-хуябы найдешь там же. Изучай!
🛠 #bash
—
✅ @bashdays / @linuxfactory / @blog
ㅤ
В этом руководстве содержится необходимая база по Bash, чтобы твой код был в кодстайле, а сами скрипты получались безопасными и предсказуемыми.
Что прикольно, это не 100500 страниц текста, это прям выжимка-концентрат.
Написал её некий Дэйв Эдди в рамках серии статей — YSAP (You Suck at Programming) или по-русски — «Ебать ты лох».
Преисполниться можно тут
Ну и из терминала можно прям почитать так:
curl style.ysap.sh
Ну а кому формат чтива не заходит, на это руководство есть видео ролики от того же иностранного гражданина.
Все ссылки на гитхабы-хуябы найдешь там же. Изучай!
Ну и совсем скоро у меня выходит брошюра, Bash для девопс-инженеров, там аналогичная выжимка-концентрат, но с упором на CI/CD и т.п. Анонс будет отдельно.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
3 114
Почему в юнитах не работает Environment.
Сразу пример:
Сделал юнит, но в скрипт
Тут снова приколы.
➡️ 1. Скрипт запускается не напрямую, а через интерпретатор.
Если в
ㅤ
Но если ты укажешь в
То переменная
Как правильно?
Вот так:
А сам скрипт:
Всё! Теперь переменная из юнита будет корректно передаваться в скрипт.
➡️ 2. Использовать EnvironmentFile=
Если у тебя дохуя переменных, то выносим их в отдельный файл, например сюда
А в самом юните прописываем:
Всё! Теперь переменные считываются из файла и скрипт их видит.
➡️ 3. Передавать переменные прямо в ExecStart
Тут особо и комментировать нечего, всё очевидно.
Как отладить и задебажить?
А вот так:
Вместо nginx подставляешь свой сервис и наблюдаешь все переменные которые передались. В наших примерах увидишь
Справедливо не только для собственных юнитов, но и для других, например для того же nginx или docker.
Как вариант, можешь добавить в скрипт такую хуйню:
И смотришь, какие переменные реально передаются в твой скрипт.
Еще одна база выдана, вроде очевидные вещи, но так глубоко в это никто не лезет. Пользуйся.
🛠 #linux #tricks #debug #systemd #bash
—
✅ @bashdays / @linuxfactory / @blog
Сразу пример:
[Service]
Environment="FOO=bar"
ExecStart=/bin/bash /usr/local/sbin/bashdays.sh
Сделал юнит, но в скрипт
bashdays.sh не передаётся переменная FOO. Хотя логически всё правильно.Тут снова приколы.
Если в
ExecStart указан скрипт с shebang’ом (#!/bin/bash), systemd запускает его как отдельный процесс, и переменные окружения передаются.ㅤ
Но если ты укажешь в
ExecStart сам интерпретатор, вот так:ExecStart=/bin/bash /usr/local/sbin/bashdays.sh
То переменная
FOO, заданная через Environment=, не попадёт в подскрипт, потому что ExecStart запускает bash, а уже bash запускает — скрипт, и переменные окружения само собой нихуя не передаются.Как правильно?
Вот так:
[Service]
Environment="FOO=bar"
ExecStart=/usr/local/sbin/bashdays.sh
А сам скрипт:
#!/bin/bash
echo "FOO is $FOO"
Всё! Теперь переменная из юнита будет корректно передаваться в скрипт.
Если у тебя дохуя переменных, то выносим их в отдельный файл, например сюда
/etc/bashdays-service.env.FOO=bar
BAZ=qux
А в самом юните прописываем:
[Service]
EnvironmentFile=/etc/bashdays-service.env
ExecStart=/usr/local/sbin/bashdays.sh
Всё! Теперь переменные считываются из файла и скрипт их видит.
[Service]
ExecStart=/bin/bash -c 'FOO=bar exec /usr/local/sbin/bashdays.sh'
Тут особо и комментировать нечего, всё очевидно.
Как отладить и задебажить?
А вот так:
systemctl show nginx
Вместо nginx подставляешь свой сервис и наблюдаешь все переменные которые передались. В наших примерах увидишь
Environment=FOO=bar.Справедливо не только для собственных юнитов, но и для других, например для того же nginx или docker.
Как вариант, можешь добавить в скрипт такую хуйню:
env > /tmp/env.log
И смотришь, какие переменные реально передаются в твой скрипт.
Еще одна база выдана, вроде очевидные вещи, но так глубоко в это никто не лезет. Пользуйся.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
7 86
Три скрытых героя systemd
А вот и сами герои:
Это невидимые юниты
➡️ 1. Timer
Про
Пример:
Активируем:
Всё! Никаких тебе легаси кронтабов, все работает на юнитах, запускается бэкапилка, тикает таймер.
➡️ 2. Path
Работает как
Пример:
Теперь каждый раз когда в папке
➡️ 3. Socket
Заменяет ручной
ㅤ
Нихуя не понятно, но на примере сейчас всё прояснится.
Пример:
В примере, сервис НЕ работает постоянно, он стартует, только когда кто-то подключается к порту
Когда ты используешь
Как понять, какой экземпляр стартует?
Зачем нужны скрытые юниты?
1. Не нужен
2. Не нужен
3. Сервисы не висят без дела, запускаются только когда нужно
4. Журналирование через
Вот такие пироги. В повседневной работе я применяю
Ну а ты попробуй хотя бы перетащить свои кронджобы в
🛠 #linux #tricks #debug #systemd #bash
—
✅ @bashdays / @linuxfactory / @blog
А вот и сами герои:
timer, path и socketЭто невидимые юниты
systemd, которые могут заменить cron, inotify`и даже `xinetd.Про
timer ты наверняка слышал. Это альтернатива crontab. Позволяет запускать сервис по расписанию. Вместо того чтобы писать крон-джобы, ты создаёшь .service и .timer.Пример:
/etc/systemd/system/backup.service
[Unit]
Description=Backup job
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup.sh
/etc/systemd/system/backup.timer
[Unit]
Description=Run backup daily
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Активируем:
sudo systemctl enable --now backup.timer
Persistent=true гарантирует запуск пропущенной задачи, если система была выключена.Всё! Никаких тебе легаси кронтабов, все работает на юнитах, запускается бэкапилка, тикает таймер.
Работает как
inotifywait или File Watcher: следит за файлами/папками и запускает сервис при изменении.Пример:
/etc/systemd/system/upload.path
[Unit]
Description=Watch upload folder
[Path]
PathModified=/var/www/bashdays/upload
Unit=process-upload.service
[Install]
WantedBy=multi-user.target
/etc/systemd/system/process-upload.service
[Unit]
Description=Process uploaded files
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/process-upload.sh
Теперь каждый раз когда в папке
/var/www/bashdays/upload что-то меняется, автоматически запускается скрипт process-upload.sh.Тут уже от твоих предпочтений зависит, как и с чем это скрещивать, но кейсов можно придумать довольно дохуя. Например, проверка антивирусом, или запуск какого-нибудь ffmpeg.
Заменяет ручной
systemctl start, активируя сервис при первом подключении к сокету. Аналог inetd/xinetdㅤ
Нихуя не понятно, но на примере сейчас всё прояснится.
Пример:
/etc/systemd/system/echo.socket
[Unit]
Description=Echo socket
[Socket]
ListenStream=12345
Accept=yes
[Install]
WantedBy=sockets.target
/etc/systemd/system/echo@.service
[Unit]
Description=Echo service
[Service]
ExecStart=/usr/bin/nc -l -p 12345 -e /bin/cat
StandardInput=socket
В примере, сервис НЕ работает постоянно, он стартует, только когда кто-то подключается к порту
12345.Когда ты используешь
.socket с Accept=yes, systemd открывает сокет сам и ждёт подключения. А когда подключение приходит — создаёт новый экземпляр сервиса, подставляя туда данные об этом соединении. После завершения — сервис умирает. Всё очень экономно и прозрачно.Как понять, какой экземпляр стартует?
systemd запускает echo@<ID>.service, где <ID> — уникальный идентификатор подключения (например, PID или номер сокета).journalctl -u echo@*
Зачем нужны скрытые юниты?
1. Не нужен
cron, всё централизовано в systemd.2. Не нужен
inotify-tools.3. Сервисы не висят без дела, запускаются только когда нужно
4. Журналирование через
journalctlВот такие пироги. В повседневной работе я применяю
timer и path, с сокетами как-то сильно не приходилось париться.Ну а ты попробуй хотя бы перетащить свои кронджобы в
timer и порадоваться бест-практикам.—
Please open Telegram to view this post
VIEW IN TELEGRAM
9 107
Продолжаем ковырять systemd
Для того чтобы потыкать
Делается это так:
Ключ
ㅤ
Теперь этот юнит будет запускаться каждый раз при старте пользовательской сессии, а таймер начнет тикать.
Эта штука создаёт символическую ссылку в
Если тебе нужно разово запустить такой юнит, то
А как отлаживать?
➡️ 1. Сначала нужно убедиться что таймер активен:
Если всё ок, то увидишь
➡️ 2. Смотрим расписание запуска
-
-
-
-
➡️ 3. Проверяем выполнялся ли сервис
Команда покажет логи выполнения скрипта. Можно сузить диапазон, например так:
По дебагу это основное. Мелочи вроде синтаксических ошибок и т.п. рассматривать не будем, тут уже от кривизны рук все зависит.
Ну а так всё что нужно тебе знать. То есть у каждого пользователя могут быть свои юниты и сервисы, а не только у рута.
🛠 #linux #tricks #debug #systemd #bash
—
✅ @bashdays / @linuxfactory / @blog
Для того чтобы потыкать
systemd не обязательно обладать рутовскими правами или судой-мудой. Да, ты не ослышался, всё можно запускать под обычным юзером.Делается это так:
systemctl --user enable bashdays.timer
Ключ
--user означает, что команда применяется в пользовательском пространстве, а не на уровне всей системы.ㅤ
Теперь этот юнит будет запускаться каждый раз при старте пользовательской сессии, а таймер начнет тикать.
Эта штука создаёт символическую ссылку в
~/.config/systemd/user/ в директории default.target.wants/Если тебе нужно разово запустить такой юнит, то
enable меняем на start. Вот и вся наука.А как отлаживать?
systemctl --user status bashdays.timer
Если всё ок, то увидишь
Active: active (waiting).systemctl --user list-timers
-
NEXT — когда следующий запуск-
LEFT — сколько осталось времени-
LAST — когда запускался в прошлый раз-
UNIT — имя таймераjournalctl --user-unit bashdays.service --since "1 hour ago"
Команда покажет логи выполнения скрипта. Можно сузить диапазон, например так:
journalctl --user-unit bashdays.service --since today
По дебагу это основное. Мелочи вроде синтаксических ошибок и т.п. рассматривать не будем, тут уже от кривизны рук все зависит.
Ну а так всё что нужно тебе знать. То есть у каждого пользователя могут быть свои юниты и сервисы, а не только у рута.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
3 62