Короче блядь! Если ты хочешь сохранить то, что ввёл человек, в переменную с именем
ㅤ
Напиши так:
А если хочешь прикрыть своё очко от спецсимволов, делай так:
Вот так делать хуёва:
В этом случае
Как говорил мой техдир:
Делай правильно и твои руки никто не оторвёт и не пропьёт.
Для маленьких:
Представь, ты говоришь: «Дай хуй» — и тебе сразу дают хуй.
Всёж просто.😲
А теперь представь, что ты говоришь: «Дай $хуй».
Тут сначала дополнительно нужно спросить «А кто такой хуй?» — может, это Олег? И тогда тебе дадут Хуй Олега.
То есть, сначала нужно узнать, кто прячется под «хуем», а потом уже брать, чтобы не взять чужой «хуй».
Надеюсь ты понял. Изучай!
🛠 #bash #badpractices #bestpractices
—
✅ @bashdays / @linuxfactory / @blog
foo
, то не нужно писать доллар ($) перед именем переменной.ㅤ
Напиши так:
read foo
А если хочешь прикрыть своё очко от спецсимволов, делай так:
IFS= read -r foo
Вот так делать хуёва:
read $foo
В этом случае
$foo
— уже не имя переменной, а значение переменной foo
. Как говорил мой техдир:
Рома, золотые у тебя руки. Оторвать бы их да пропить.
Делай правильно и твои руки никто не оторвёт и не пропьёт.
Для маленьких:
Представь, ты говоришь: «Дай хуй» — и тебе сразу дают хуй.
Всёж просто.
А теперь представь, что ты говоришь: «Дай $хуй».
Тут сначала дополнительно нужно спросить «А кто такой хуй?» — может, это Олег? И тогда тебе дадут Хуй Олега.
То есть, сначала нужно узнать, кто прячется под «хуем», а потом уже брать, чтобы не взять чужой «хуй».
Надеюсь ты понял. Изучай!
—
Please open Telegram to view this post
VIEW IN TELEGRAM
3 69
Все знают команду
🔤 🔤 🔥 🔤 🔤 🔤 🔤
Очень удобная команда, имеет очень много форматов и позволяет складывать/вычитать даты, например:
Но в bash есть еще средства для работы со временем:
ㅤ
Переменная
Переменная
Обратите внимание, число секунд не зависит от часового пояса.
Обратите внимание, разделитель дробной части может быть различным, в зависимости от языковых настроек.
Переменные
И еще есть специальный формат оператора
Например:
Если не указать переменную или указать -1 - будет использовано текущая дата/время. Если указать -2 - будет использовано время начала работы этого экземпляра bash.
И
Например:
Полный список временны́х зон на системах с
🛠 #bash
—
✅ @bashdays / @linuxfactory / @blog
date
для работы с датами/временем.Очень удобная команда, имеет очень много форматов и позволяет складывать/вычитать даты, например:
date -d"-1 day" # Чт 02 янв 2025 21:52:15 MSK - текущая дата минус один день.
date -d"+2 week" # Пт 17 янв 2025 21:53:48 MSK - текущая дата плюс две недели.
"day", "week" "month", "year", "hour", "min", "sec"
Но в bash есть еще средства для работы со временем:
ㅤ
Переменная
SECONDS
показывает, сколько секунд прошло с начала запуска скрипта или момента задания значения переменной. Обычно используется для хронометража блока кода скрипта. Например:SECONDS=0
# здесь блок длительного кода
echo $SECONDS
Переменная
EPOCHSECONDS
- показывает число секунд, прошедших с начала эпохи UNIX (01.01.1970 00:00:00 UTC).Обратите внимание, число секунд не зависит от часового пояса.
EPOCHREALTIME
- аналогична предыдущей, но секунды с точностью до микросекунды. С помощью этой переменной можно точно отслеживать время небольших кусков кода. Даже если просто вывести переменную дважды - будет разное время:echo $EPOCHREALTIME $EPOCHREALTIME
# 1744652451,166523 1744652451,166539
Обратите внимание, разделитель дробной части может быть различным, в зависимости от языковых настроек.
Переменные
EPOCHSECONDS
и EPOCHREALTIME
обнулить не получится.И еще есть специальный формат оператора
printf
для преобразования секунд UNIX в формат дат:printf '%(DT_FORMAT)T' $EPOCHSECONDS
DT_FORMAT
- практически полностью совпадает с форматом команды date
.Например:
printf '%(%Y%m%d-%H%M%S)T' $EPOCHSECONDS
# 20250414-203214
Если не указать переменную или указать -1 - будет использовано текущая дата/время. Если указать -2 - будет использовано время начала работы этого экземпляра bash.
printf
удобен тем, что выполняется гораздо быстрее чем date
(хотя говорить о скорости bash немного смешно) и позволяет результат операции записать сразу в переменную (Например DT).printf -v DT '%(%Y%m%d-%H%M%S)T'
И
date
и printf
работают в локальной временно́й зоне, чтобы это изменить можно задать "врéменную переменную" TZ.Например:
TZ='UTC' date
# Пн 14 апр 2025 16:15:57 UTC
TZ='Asia/Irkutsk' printf '%(%Y%m%d-%H%M%S)T\n'
# 20250415-001621
Полный список временны́х зон на системах с
systemd
можно получить с помощью команды timedatectl list-timezones
.—
Please open Telegram to view this post
VIEW IN TELEGRAM
5 57
Всё тебе, блядь, смехуюшечки да пиздахаханьки.
ㅤ
Создаём файл:
Внутри получаем:
И применяем бэд-практику:
Эта команда читает файл
Что тут не так?
А всё! В лучшем случае получишь хуем по лбу и примеришь золотой пизды колпак. Эта команда испортит файл. Низя так делать! Всё проебёшь!
После этой команды файл
А теперь делаем правильно!
Смотрим содержимое файла
Здесь мы всё сделали через временный файл, громоздко, но по крайней мере безопасно. Веселый колпак тебе теперь точно не светит.
Можно извратиться и провернуть всё это дело на основе дескрипторов, но опять же этот способ не безопасен.
Можно правда хакнуть таким методом:
Это работает без временного файла на уровне Bash, потому что
Резюмируем: Не ссы использовать временные файлы, пусть твои bash скрипты будут безопасны и читаемы.
🛠 #bash #badpractices #bestpractices
—
✅ @bashdays / @linuxfactory / @blog
ㅤ
Создаём файл:
echo -e "foo\nbar\nfoo again" > bashdays.txt
Внутри получаем:
foo
bar
foo again
И применяем бэд-практику:
cat bashdays.txt | sed 's/foo/baz/g' > bashdays.txt
Эта команда читает файл
bashdays.txt
и одновременно пишет в него.Что тут не так?
А всё! В лучшем случае получишь хуем по лбу и примеришь золотой пизды колпак. Эта команда испортит файл. Низя так делать! Всё проебёшь!
После этой команды файл
bashdays.txt
обнулится. Хотя визуально команда выглядит абсолютно безопасной.А теперь делаем правильно!
sed 's/foo/baz/g' bashdays.txt > tmpfile && mv tmpfile bashdays.txt
Смотрим содержимое файла
bashdays.txt
и видим ожидаемый результат:baz
bar
baz again
Здесь мы всё сделали через временный файл, громоздко, но по крайней мере безопасно. Веселый колпак тебе теперь точно не светит.
Можно извратиться и провернуть всё это дело на основе дескрипторов, но опять же этот способ не безопасен.
Можно правда хакнуть таким методом:
printf '%s\n' ',s/foo/baz/g' w q | ed -s bashdays.txt
Это работает без временного файла на уровне Bash, потому что
ed
сделаем всё сам. Но опять же конструкция нихуя непонятная. Резюмируем: Не ссы использовать временные файлы, пусть твои bash скрипты будут безопасны и читаемы.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
1 64
Нежных много, деловых мало. Ну не могу я из песни слов выбросить, получится сухо и не интересно.
ㅤ
Поэтому нарушать традиций не будем, продолжаем в том же ключе. А нежные пусть идут на... ну ты понял.
Рассмотрим распространённый случай:
Тут всё предельно ясно и понятно. Все в ажуре!
Но это вершина айсберга. Эта команда может отрабатывать не так, как ты ожидаешь. Потому что
Что подразумевается по «хитрыми штуками»
— Разделить содержимое на отдельные слова (если там есть пробелы).
— Подставить имена файлов с таким шаблоном, если он выглядит как
И тут у новичков возникает многочасовой проёб, когда переменная выводит неправильные значения.
Смотри:
Выглядит опять всё красиво. А по факту получаешь:
Да блядь! А я просто хотел вывести строкой
Первое
Поэтому, если ты хочешь наверняка вывести переменную как есть, лучше используй `printf`
Для маленьких:
Представь, что ты говоришь падшей женщине: «покажи что у тебя в трусах», она снимает трусы, а там — большой сочный хуй.
А чтобы в трусах был ожидаемый результат, нужно изначально это учесть в моменте знакомства (до просьбы) и расставить все кавычки.
Хотя у каждого свой ожидаемый результат… возможно кавычки вовсе и не потребуются.
Такие дела. Изучай!
🛠 #bash #badpractices #bestpractices
—
✅ @bashdays / @linuxfactory / @blog
ㅤ
Поэтому нарушать традиций не будем, продолжаем в том же ключе. А нежные пусть идут на... ну ты понял.
Рассмотрим распространённый случай:
echo $foo
Тут всё предельно ясно и понятно. Все в ажуре!
Но это вершина айсберга. Эта команда может отрабатывать не так, как ты ожидаешь. Потому что
foo
это переменная. И если ты её не возьмешь в кавычки, то Bash может сделать с её содержимым всякие хитрые штуки.Что подразумевается по «хитрыми штуками»
— Разделить содержимое на отдельные слова (если там есть пробелы).
— Подставить имена файлов с таким шаблоном, если он выглядит как
*.zip
(это называется глоббинг).И тут у новичков возникает многочасовой проёб, когда переменная выводит неправильные значения.
Смотри:
msg="Пожалуйста, введите имя файла в формате *.zip"
echo $msg
Выглядит опять всё красиво. А по факту получаешь:
Пожалуйста, введите имя файла в формате awscliv2.zip
Да блядь! А я просто хотел вывести строкой
*.zip
а не получить список файлов в текущей папке.var="*.zip"
echo "$var"
echo $var
Первое
echo
напечатает *.zip
, а второе выведет список всех файлов, которые заканчиваются на .zip.Поэтому, если ты хочешь наверняка вывести переменную как есть, лучше используй `printf`
printf "%s\n" "$foo"
Для маленьких:
Представь, что ты говоришь падшей женщине: «покажи что у тебя в трусах», она снимает трусы, а там — большой сочный хуй.
А чтобы в трусах был ожидаемый результат, нужно изначально это учесть в моменте знакомства (до просьбы) и расставить все кавычки.
Хотя у каждого свой ожидаемый результат… возможно кавычки вовсе и не потребуются.
Такие дела. Изучай!
—
Please open Telegram to view this post
VIEW IN TELEGRAM
1 61
Живу в лесу, поклоняюсь колесу…
ㅤ
А чё эта хуйня не работает? Яж всё правильно сделал!
С виду всё верно. Но если внимательно прочитать команду и подумать, происходит такое:
Но у
А
Поэтому правильно так:
Теперь логика не хромает:
Вроде мелочь, а на эти грабли постоянно наступают и бегут — аа, у меня принтер не печатает!!! Что я делаю не так???
🛠 #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 111
Почему в юнитах не работает 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 82
Три скрытых героя 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 101
Продолжаем ковырять 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 58
Продолжаем разбирать
ㅤ
Директива
Пример:
Этот юнит не запустится, если файл
Если путь НЕ существует,
Нахуя это надо?
1. Например, если
2. Можно создавать один юнит, который активируется только при наличии определённого модуля или плагина.
3. Иногда это используют в early boot-юнитах, чтобы запускать их только если что-то доступно (например, том LUKS).
Список основных Condition's (нажми на спойлер)
Дополнительно
Если заменить
То есть берем к примеру директиву
И получается:
Если
Статус будет такой:
Ну и все это дело можно комбинировать:
Если
Если существует, но не смонтирован — юнит заваливается.
Короче
🛠 #linux #tricks #debug #systemd #bash
—
✅ @bashdays / @linuxfactory / @blog
systemd
на кирпичики. Сегодня про кандишены (условия/ситуации).ㅤ
Директива
ConditionPathExists
используется в юнит-файлах systemd
и позволяет задать условие для запуска юнита: он будет запущен только если указанный файл или директория существует.Пример:
[Unit]
Description=Special Service
ConditionPathExists=/etc/bashdays-config
[Service]
ExecStart=/usr/local/bin/bashdays-handler
Этот юнит не запустится, если файл
/etc/bashdays-config
не существует. В статусе сервиса ты увидишь:ConditionPathExists=/etc/bashdays-config was not met
Если путь НЕ существует,
systemd
не будет запускать юнит. Вместо этого он будет считаться пропущенным (skipped), а не проваленным.Нахуя это надо?
1. Например, если
bashdays-config
существует, запускается сервис с особым поведением.2. Можно создавать один юнит, который активируется только при наличии определённого модуля или плагина.
3. Иногда это используют в early boot-юнитах, чтобы запускать их только если что-то доступно (например, том LUKS).
Список основных Condition's (нажми на спойлер)
ConditionPathExists — файл или каталог существует
ConditionPathIsDirectory — путь существует и это каталог
ConditionPathIsMountPoint — путь является точкой монтирования
ConditionFileIsExecutable — файл существует и он исполняемый
ConditionKernelCommandLine — есть ли параметр ядра с указанным значением
ConditionPathExistsGlob — совпадает ли хотя бы один путь по glob-шаблону
ConditionPathIsSymbolicLink — является ли путь символической ссылкой
ConditionFileNotEmpty — существует ли файл и не пуст ли он
ConditionEnvironment — установлена ли переменная окружения
ConditionArchitecture — архитектура CPU (например, x86_64, aarch64)
ConditionVirtualization — nип виртуализации (например, kvm, docker)
ConditionHost — имя хоста
ConditionMachineID — cовпадает ли machine-id
ConditionControlGroupController — есть ли указанный cgroup controller (например, cpu, memory)
ConditionNeedsUpdate — нуждается ли в обновлении (/usr или /etc)
ConditionFirstBoot — Первый ли это запуск после установки
ConditionACPower — Подключено ли питание от сети (для ноутбуков)
ConditionSecurity — Активен ли определённый LSM (например, selinux)
ConditionUser — Запускается ли от указанного пользователя
ConditionGroup — Запускается ли от указанной группы
ConditionCapability — Имеет ли процесс определённую capability
ConditionNetwork — Есть ли сеть (online, configured)
ConditionMemory — Есть ли минимум указанного объёма памяти
Дополнительно
Если заменить
Condition
на Assert
, условие не выполнено — юнит считается проваленным, а не пропущенным.То есть берем к примеру директиву
ConditionPathExists
и меняем её на AssertPathExists
. AssertPathExists=/etc/bashdays.conf
И получается:
ConditionPathExists
— юнит пропускается (не считается ошибкой)AssertPathExists
— юнит падает (считается ошибкой)Assert
полезен, когда ты строго требуешь, чтобы ресурс (например, внешний диск, NFS, или другой том) был смонтирован перед запуском сервиса. Если его нет — это ошибка, а не «ну и похуй».[Unit]
Description=Start backup script only if /mnt/backup is mounted
AssertPathIsMountPoint=/mnt/backup
[Service]
ExecStart=/usr/local/bin/backup.sh
Если
/mnt/backup
не смонтирован, systemd
выдаст ошибку, и сервис не запустится.Статус будет такой:
systemd[1]: Starting backup.service...
systemd[1]: backup.service: Failed with result 'assert'.
Ну и все это дело можно комбинировать:
ConditionPathExists=/mnt/backup
AssertPathIsMountPoint=/mnt/backup
Если
/mnt/backup
не существует — юнит пропускается.Если существует, но не смонтирован — юнит заваливается.
Короче
systemd
не такой уж простой, как кажется с первого взгляда. На нём можно прям заебись логику построить и получить желаемое. Так что недооценивать его явно не стоит, это прям заебись комбайн.—
Please open Telegram to view this post
VIEW IN TELEGRAM
6 70
Всем привет. Опять решил поделиться опытом своих провалов.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
Вобщем, как-то раз я решил написать самоучитель по bash.
По традиции все учебники либо в html, либо в pdf. Я решил остановиться на pdf. Я начал писать в libreoffice, там есть экспорт в pdf. Пока писал - познакомился с markdown. И вдруг подумалось, markdown - такой классный, напрямик можно в гитхаб запихнуть. И в pdf конвертнуть с помощью pandoc можно. Вобщем, решил верстать в markdown.
ㅤ
Для начала познакомился с редакторами, которые умеют подсвечивать markdown (pluma, mousepad, geany) Вобщем, жить можно, но подсвечивается не всё.
После этого попробовал Apostrophe. Он уже умеет дополнительно показывать результат. Но работает медленно. Особенно на больших документах.
В общем - перепопробовал, - не нравится. Решил писать в mousepad, а потом конвертить с помощью pandoc.
Написал скрипт для удобства пользования - меняется дата файла - автоматом резервная копия версии и конвертация в фоне.
Начал работать и тут вскрылась куча недостатков markdown;
1. Нет нормального форматирования (заголовок нельзя центрировать)
2. Нет нормальных таблиц, один ущерб какой-то.
3. Проблема с кирилицей (решаемая), но с помощью дополнительного tex-файла настроек.
4. Нет возможности печатать некоторые символы UTF.
5. некоторые символы приходится маскировать, чтобы они не понимались, как управляющие.
Единственный плюс - подсветка синтаксиса bash.
В общем, после 19 главы я понял, что моего терпения не хватит закончить работу. Вернулся к libreoffice. За два дня освоил работу со стилями, и все пошло как по маслу. Кстати, экспорт в pdf тоже быстрее, на мой взгляд.
Может и не все получилось, сами знаете - первый блин комом.
Замечания и комментарии приветствуются.
С результатом можно ознакомиться здесь 👇
🅰️ 🅰️
➡️ https://github.com/tagd-tagd/self-instruction
🅰️ 🅰️
🛠 #bash #linux #utilites #markdown
—
✅ @bashdays / @linuxfactory / @blog
Вобщем, как-то раз я решил написать самоучитель по bash.
По традиции все учебники либо в html, либо в pdf. Я решил остановиться на pdf. Я начал писать в libreoffice, там есть экспорт в pdf. Пока писал - познакомился с markdown. И вдруг подумалось, markdown - такой классный, напрямик можно в гитхаб запихнуть. И в pdf конвертнуть с помощью pandoc можно. Вобщем, решил верстать в markdown.
ㅤ
Для начала познакомился с редакторами, которые умеют подсвечивать markdown (pluma, mousepad, geany) Вобщем, жить можно, но подсвечивается не всё.
После этого попробовал Apostrophe. Он уже умеет дополнительно показывать результат. Но работает медленно. Особенно на больших документах.
В общем - перепопробовал, - не нравится. Решил писать в mousepad, а потом конвертить с помощью pandoc.
Написал скрипт для удобства пользования - меняется дата файла - автоматом резервная копия версии и конвертация в фоне.
Начал работать и тут вскрылась куча недостатков markdown;
1. Нет нормального форматирования (заголовок нельзя центрировать)
2. Нет нормальных таблиц, один ущерб какой-то.
3. Проблема с кирилицей (решаемая), но с помощью дополнительного tex-файла настроек.
\usepackage{longtable}\setlength{\LTleft}{2em}
\setmainfont{Liberation Serif}
\setsansfont{Liberation Sans}
\setmonofont{Liberation Mono}
\newfontfamily\cyrillicfont{Liberation Sans}
\defaultfontfeatures{Scale=MatchLowercase, Mapping=tex-text}
\usepackage{polyglossia}
\setmainlanguage{russian}
\setotherlanguage{english}
4. Нет возможности печатать некоторые символы UTF.
5. некоторые символы приходится маскировать, чтобы они не понимались, как управляющие.
Единственный плюс - подсветка синтаксиса bash.
В общем, после 19 главы я понял, что моего терпения не хватит закончить работу. Вернулся к libreoffice. За два дня освоил работу со стилями, и все пошло как по маслу. Кстати, экспорт в pdf тоже быстрее, на мой взгляд.
Может и не все получилось, сами знаете - первый блин комом.
Замечания и комментарии приветствуются.
С результатом можно ознакомиться здесь 👇
—
Please open Telegram to view this post
VIEW IN TELEGRAM
3 93
Сегодня будем убивать неугодные сервисы. Нет, тут будет не про
Короче в юните можно указать параметр
ㅤ
Удобно применять для сервисов, которые не должны жить вечно. Нет ничего вечного! Например, временные задачи, вспомогательные демоны, скрипты и т.п.
А что будет если время вышло?
Как и написал выше — будет песда!
Этот сервис будет убит через 60 секунд после запуска — даже если скрипт ещё не завершился.
Если
Ждём до 10 секунд → если не завершился →
Частый паттерн
Сервис работает максимум 5 минут, и если завис —
А нахуя тут Restart=on-failure?
Оно говорит — «если сервис завершился аварийно — перезапусти его» А завершение по
Если не указать
Важный нюанс!
Если в юните используется
Такие дела, изучай!
🛠 #linux #tricks #debug #systemd #bash
—
✅ @bashdays / @linuxfactory / @blog
kill
и т.п. а будет все тот же systemd
.Короче в юните можно указать параметр
RuntimeMaxSec
, в нем задаём время жизни сервиса. Если сервис работает дольше указанного времени, то ему песда. Systemd
принудительно завершит его.ㅤ
Удобно применять для сервисов, которые не должны жить вечно. Нет ничего вечного! Например, временные задачи, вспомогательные демоны, скрипты и т.п.
[Service]
RuntimeMaxSec=30s
0s
, 5min
, 1h
, 2d
— интервалыinfinity
— отключение лимитаА что будет если время вышло?
Как и написал выше — будет песда!
Systemd
пошлет SIGTERM
. А если сервис сука живучий и не завершился, то в ход пойдет тяжелая артиллерия через TimeoutStopSec
, тут уже будет послан SIGKILL
. Но его нужно предварительно прописать.TimeoutStopSec=
— это время ожидания корректного завершения сервиса после того, как systemd послал ему сигнал SIGTERM
.[Service]
ExecStart=/usr/bin/python3 /opt/scripts/bashdays-task.py
RuntimeMaxSec=60
Этот сервис будет убит через 60 секунд после запуска — даже если скрипт ещё не завершился.
[Service]
ExecStart=/usr/bin/bashdays-daemon
RuntimeMaxSec=60
TimeoutStopSec=10
Если
bashdays-daemon
работает дольше 60 секунд → SIGTERM
Ждём до 10 секунд → если не завершился →
SIGKILL
Частый паттерн
RuntimeMaxSec=300
TimeoutStopSec=5
Restart=on-failure
Сервис работает максимум 5 минут, и если завис —
systemd
подождёт 5 секунд на аккуратное завершение, а потом прибьёт его нахуй.А нахуя тут Restart=on-failure?
Оно говорит — «если сервис завершился аварийно — перезапусти его» А завершение по
SIGKILL
из-за превышения времени жизни это и есть failure.Если не указать
TimeoutStopSec
, будет использоваться значение по умолчанию: 90 секунд — порой это дохуя, поэтому предпочтительнее задать его руками.Важный нюанс!
Если в юните используется
Restart=always
, то после убийства, сервис будет перезапущен и возможно сразу «помрёт» если не изменит своё поведение.Такие дела, изучай!
—
Please open Telegram to view this post
VIEW IN TELEGRAM
6 59
Имена файлов удаляются с помощью системных вызовов
ㅤ
Ну дак вот. Когда счетчик ссылок (имен) становится равен нулю — файл удаляется.
Физическое удаление (блоки маркируются свободными) происходит когда больше никто не удерживает файл. То есть процесс закрыл последний дескриптор ассоциированный с файлом.
Давай попробуем поймать (открыть файл) до того, как запустится вызов
Создаем искусственный временный файл:
Создаем и запускаем скрипт:
Что тут происходит?
Итоги:
Эта фишка демонстрирует, что можно удалить файл, но продолжить его использовать если он был открыт на момент удаления.
Это классическая Unix-модель: имя файла — не сам файл, а просто ссылка.
А тут как-то рассказывал, как восстанавливать удаленные файлы, в том числе и запущенные.
Чтиво почитать:
man 2 unlink
man 2 unlinkat
🛠 #linux #bash
—
✅ @bashdays / @linuxfactory / @blog
unlink
, unlinkat
.ㅤ
Ну дак вот. Когда счетчик ссылок (имен) становится равен нулю — файл удаляется.
Физическое удаление (блоки маркируются свободными) происходит когда больше никто не удерживает файл. То есть процесс закрыл последний дескриптор ассоциированный с файлом.
Давай попробуем поймать (открыть файл) до того, как запустится вызов
unlink
.Создаем искусственный временный файл:
printf '%s\n' {a..z} > /tmp/sh-thd-bashdays
Создаем и запускаем скрипт:
#!/bin/bash
file=/tmp/sh-thd-bashdays
exec 3< "$file"
rm -vf "$file" # Имени уже нет
read -N 4096 -ru 3 # Но мы читаем
printf '%s' $REPLY # Выводим
exec 3>&-
exit
Что тут происходит?
exec 3<
- файл открывается на чтение, привязывается к дескриптору 3
и с этого момента файл начинает жить в «памяти» в open file table ядра, даже если мы его ёбнем удалим. Дескриптор 3
теперь указывает на открытый файл, независимо от имени.rm -vf
- файл удаляется из файловой системы, имени больше нет, НО, его содержимое еще доступно, потому что он открыт.read -N 4096
- читаем до 4096 байт из открытого файла через дескриптор 3
.exec 3>&
- закрываем дескриптор 3
и после этого файл окончательно исчезнет, если его больше никто не держит, а ядро высвобождает ресурсы.Итоги:
Эта фишка демонстрирует, что можно удалить файл, но продолжить его использовать если он был открыт на момент удаления.
Это классическая Unix-модель: имя файла — не сам файл, а просто ссылка.
Если держишь дескриптор — файл всё ещё существует.
А тут как-то рассказывал, как восстанавливать удаленные файлы, в том числе и запущенные.
Чтиво почитать:
man 2 unlink
man 2 unlinkat
—
Please open Telegram to view this post
VIEW IN TELEGRAM
Напозырить и ужаснуться
50 GNU Commands VS 50 PowerShell Commands
ㅤ
🛠 #bash #powershell
—
✅ @bashdays / @linuxfactory / @blog
50 GNU Commands VS 50 PowerShell Commands
ㅤ
—
Please open Telegram to view this post
VIEW IN TELEGRAM
2 38
CRON в первый рабочий день месяца
ㅤ
Да, ключевой момент тут именно в «рабочий день».
Задачка вроде не тривиальная, но как оказалось
Мы с тобой не пальцем деланы, изобретем свой велосипед!
Чтобы реализовать желаемое, я изобрел такую кишку:
Выглядит монструозно, но блядь... Можно в сам скрипт проверку забить, но это еще один костыль.
Что тут происходит?
Короче…
То есть АПИха возвращает
Тут еще бы для
Всегда старайся указывать полные пути до программ и утилит. Это распространенный кейс, крон вроде отрабатывает, но ничего не работает. А оно просто не знает откуда запускать, то что ты ему прописал.
Если знаешь еще какие-то способы, велком в комментарии.
UPD: более корректные алгоритмы решения этой задачи смотрим в комментах, всем спасибо!
🛠 #bash #linux
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
ㅤ
Да, ключевой момент тут именно в «рабочий день».
Задачка вроде не тривиальная, но как оказалось
cron
нихуя не умеет определять рабочий сегодня день или нет. И systemd timer
тоже не умеет. Никто ничо блядь не умеет.Мы с тобой не пальцем деланы, изобретем свой велосипед!
Чтобы реализовать желаемое, я изобрел такую кишку:
0 8 1-3 * * [ "$(date +\%u)" -lt 6 ] && [ "$(curl -s https://isdayoff.ru/$(date +\%Y\%m\%d))" = "0" ] && /usr/local/sbin/bashdays.sh
Выглядит монструозно, но блядь... Можно в сам скрипт проверку забить, но это еще один костыль.
Что тут происходит?
Короче…
0 8 1-3 * *
— Запуск 1–3 числа месяца в 08:00. Потому что первый рабочий день месяца может выпасть на 1-е число, если это будний день. 2-е число, если 1-е — выходной. 3-е число, если 1-е и 2-е — выходные (например, сб + вс).[ "$(date +%u)" -lt 6 ]
— Проверяем, что сегодня будний день (1–5 = Пн–Пт). Пусть тебя здесь 6ка не смущает. Она означает: день меньше 6, т.е. 1
, 2
, 3
, 4
, 5
— это будни (Пн – Пт).curl ... = "0"
— Проверка, что сегодня рабочий день по календарю РФ, выполняется через АПИху производственного календаря.&& /opt/scripts/first-workday.sh
— Выполняем только если сошлись оба условия.То есть АПИха возвращает
0
если сегодня будни. Если выходной, то оно вернет что-то другое.Тут еще бы для
curl
и date
указать что-то вроде CURL=$(which curl)
, а то крон частенько залупается и не знает откуда запускать эти команды. Всегда старайся указывать полные пути до программ и утилит. Это распространенный кейс, крон вроде отрабатывает, но ничего не работает. А оно просто не знает откуда запускать, то что ты ему прописал.
Да, есть зависимость от внешнего API и порой это пиздец хуёва. Отвалилась АПИха, скрипт не получил данные. Поэтому рекомендую сделать себе собственный микросервис с АПИхой и ни от кого не зависеть. Тем более если сервер в оффлайне, запрос на внешнюю АПИху он сделать не сможет.
Если знаешь еще какие-то способы, велком в комментарии.
UPD: более корректные алгоритмы решения этой задачи смотрим в комментах, всем спасибо!
—
Please open Telegram to view this post
VIEW IN TELEGRAM
6 56
Сопроцессы. Теория. Часть первая.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
Надеюсь все знают, что в bash можно запустить любую программу в фоновом режиме. Для этого после команды достаточно указать &. Например: sleep &
ㅤ
В этом случае программа начинает жить своей жизнью а переменная $! получает PID последнего запущенного процесса, чтобы можно было отследить завершение фонового процесса, а при крайней необходимости сказать знаменитую фразу Тараса Бульбы.
Для контроля и управления фоновыми заданиями служат команды jobs bg fg .
Если будет интересно - напишите комменты - рассмотрю их подробнее. Сейчас не об этом. В bash, начиная с четвертой версии появились сопроцессы (coproc). Иногда их называют копроцессы, но мне слово не нравится. Чем то оно попахивает.
С помощью этой команды можно запустить любой процесс в фоновом режиме, но в отличие от обычного фонового режима, процесс запущенный через coproc связывается через дескрипторы с основной оболочной и с ним можно взаимодействовать.
Форма запуска:
NAME - имя индексного массива, в котором содержатся дескрипторы command. Если NAME не задано, по-умолчанию используется COPROC .
COPROC[0] связан с stdout command , COPROC[1] - с stdin. И с помощью этих дескрипторов мы сможем взаимодействовать с command, не смотря на то, что программа работает в фоновом режиме.
Сразу хочу обратить внимание. Если задано NAME - вместо command необходимо использовать группировку, даже из одной команды:
Иначе coproc поймет NAME - как команду, а command как ее параметры. И да, при группировке пробелы и точка с запятой очень важны. Добро пожаловать в bash. :-)
Пока все не так просто, как хочется, но практика все расставит по местам, а заодно научимся принимать почту от POP3 сервера с ssl.
🛠 #bash
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
Надеюсь все знают, что в bash можно запустить любую программу в фоновом режиме. Для этого после команды достаточно указать &. Например: sleep &
ㅤ
В этом случае программа начинает жить своей жизнью а переменная $! получает PID последнего запущенного процесса, чтобы можно было отследить завершение фонового процесса, а при крайней необходимости сказать знаменитую фразу Тараса Бульбы.
Для контроля и управления фоновыми заданиями служат команды jobs bg fg .
Если будет интересно - напишите комменты - рассмотрю их подробнее. Сейчас не об этом. В bash, начиная с четвертой версии появились сопроцессы (coproc). Иногда их называют копроцессы, но мне слово не нравится. Чем то оно попахивает.
С помощью этой команды можно запустить любой процесс в фоновом режиме, но в отличие от обычного фонового режима, процесс запущенный через coproc связывается через дескрипторы с основной оболочной и с ним можно взаимодействовать.
Форма запуска:
coproc [NAME] command [redirections]
NAME - имя индексного массива, в котором содержатся дескрипторы command. Если NAME не задано, по-умолчанию используется COPROC .
COPROC[0] связан с stdout command , COPROC[1] - с stdin. И с помощью этих дескрипторов мы сможем взаимодействовать с command, не смотря на то, что программа работает в фоновом режиме.
Сразу хочу обратить внимание. Если задано NAME - вместо command необходимо использовать группировку, даже из одной команды:
coproc [NAME] { command [redirections];}
Иначе coproc поймет NAME - как команду, а command как ее параметры. И да, при группировке пробелы и точка с запятой очень важны. Добро пожаловать в bash. :-)
Пока все не так просто, как хочется, но практика все расставит по местам, а заодно научимся принимать почту от POP3 сервера с ssl.
—
Please open Telegram to view this post
VIEW IN TELEGRAM
2 39
Сопроцессы. Практика. Часть Вторая.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
coproc хорошо подходит для общения в клиент-серверном режиме. Для примера попробуем подключиться к POP3 серверу с шифрованием ssl прямо из bash-скрипта.
ㅤ
Сам ssl несколько сложноват для bash, поэтому в качестве посредника будем использовать openssl s_client.
Протокол и команды POP3 лучше посмотреть на википедии.
1. Cоздим сопроцесс. Для этого запустим openssl в режиме s_client. При этом из дескриптора POP3_CONN[0] можно читать данные от сопроцесса.
В дескриптор POP3_CONN[1] можно писать для сопроцесса.
При записи используем перенаправление >&${POP3_CONN[1] . При чтении тоже можно использовать перенаправление, но поскольку у команды read есть ключ -u красивее воспользоваться им.
2. Аутентифицируемся
3. Закроем сессию и дескрипторы.
Задержки 0.3 секунды при отправке нужны для того, чтобы сервер успел сформировать ответ.
Ошибки -ERR не обрабатывал. В случае чего команда read завершится по таймауту в 2 сек. (-t 2)
${REC//$'\r'/} конструкция удаляет cr, потому что POP3 сервер отвечает c lfcr.
help coproc
help read
man openssl
вики POP3
🛠 #bash #linux
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
coproc хорошо подходит для общения в клиент-серверном режиме. Для примера попробуем подключиться к POP3 серверу с шифрованием ssl прямо из bash-скрипта.
ㅤ
Сам ssl несколько сложноват для bash, поэтому в качестве посредника будем использовать openssl s_client.
Протокол и команды POP3 лучше посмотреть на википедии.
1. Cоздим сопроцесс. Для этого запустим openssl в режиме s_client. При этом из дескриптора POP3_CONN[0] можно читать данные от сопроцесса.
В дескриптор POP3_CONN[1] можно писать для сопроцесса.
При записи используем перенаправление >&${POP3_CONN[1] . При чтении тоже можно использовать перенаправление, но поскольку у команды read есть ключ -u красивее воспользоваться им.
2. Аутентифицируемся
3. Закроем сессию и дескрипторы.
# Функция для отправки команд серверу
function SEND_CMD() {
sleep 0.3
echo "$@" >&${POP3_CONN[1]}
sleep 0.3
}
# аутентификация. Обычный логин
function POP3_LOGIN() {
declare REC
declare -a AREC
# проверка соединения
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
# Отправляем логин
SEND_CMD "USER $USER"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
# Отправляем пароль
SEND_CMD "PASS $PASS"
read -ert 2 -u "${POP3_CONN[0]}" REC
read -ra AREC <<<${REC//$'\r'/}
if [[ "${AREC[0]}" == "+OK" ]];then
return 0 # аутентификация успешна
else
return 3 # не правильный пароль
fi
else
return 2 #не правильный login
fi
else
return 1 # ошибка соединения с сервером
fi
}
#Выход и закрытие дескрипторов.
function POP3_QUIT(){
SEND_CMD "QUIT"
# Закрываем coproc
exec ${POP3_CONN[0]}<&-
exec ${POP3_CONN[1]}>&-
}
Задержки 0.3 секунды при отправке нужны для того, чтобы сервер успел сформировать ответ.
Ошибки -ERR не обрабатывал. В случае чего команда read завершится по таймауту в 2 сек. (-t 2)
${REC//$'\r'/} конструкция удаляет cr, потому что POP3 сервер отвечает c lfcr.
#!/bin/bash
SERVER="server"
PORT=995
USER="user@server"
PASS="StrongPass"
# создаем сопроцесс и соединяемся с сервером pop3
coproc POP3_CONN { openssl s_client -connect "${SERVER}:${PORT}" -quiet 2>/dev/null;}
POP3_LOGIN
POP3_QUIT
help coproc
help read
man openssl
вики POP3
—
Please open Telegram to view this post
VIEW IN TELEGRAM
2 21
Сопроцессы. Практика. Часть Третья.
🔤 🔤 🔥 🔤 🔤 🔤 🔤
Это уже больше не сопроцессы, а про то, как принять почту в скрипте bash.
ㅤ
Соединение с POP3 сервером есть. Аутентификация тоже. Осталось написать что-нибудь полезное.
Финальный код
help coproc
help read
man openssl
🛠 #bash #linux
—
✅ @bashdays ✅ @linuxfactory ✅ @blog
Это уже больше не сопроцессы, а про то, как принять почту в скрипте bash.
ㅤ
Соединение с POP3 сервером есть. Аутентификация тоже. Осталось написать что-нибудь полезное.
# возвращает число писем в ящике
function POP3_STAT(){
declare -a AREC
declare REC
SEND_CMD "STAT"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
echo ${AREC[1]} # число сообщений
return 0
else
echo 0
return 1
fi
}
#Помечает к удалению указанное письмо
function POP3_DELE(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -a AREC
declare REC
SEND_CMD "DELE $MSG_NUM" #удаляем указанное сообщение
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
return 0
else
return 1
fi
}
# читает письмо с заголовками
function POP3_RETR(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -a AREC
declare REC
SEND_CMD "RETR $MSG_NUM" #читаем указанное сообщение
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
while read -r -t 2 -u ${POP3_CONN[0]} REC ; do
REC=${REC//$'\r'/}
echo "$REC"
if [[ "$REC" == "." ]];then
return 0 # msg end
fi
done
else
return 1
fi
}
# читает указанное число строк письма
function POP3_TOP(){
declare -i MSG_NUM=${1:-1} # по умолчанию первое
declare -i STR_NUM=${2:-1} # по умолчанию одна строка
declare -a AREC
declare REC
#читаем указанное сообщение
SEND_CMD "TOP $MSG_NUM $STR_NUM"
read -ert 2 -u ${POP3_CONN[0]} REC
read -ra AREC <<<${REC//$'\r'/}
if [[ ${AREC[0]} == "+OK" ]];then
while read -ert 2 -u ${POP3_CONN[0]} REC ; do
REC=${REC//$'\r'/}
echo "$REC"
if [[ "$REC" == "." ]];then
return 0
fi
done
else
return 1
fi
}
Финальный код
#!/bin/bash
SERVER="server"
PORT=995
USER="user@server"
PASS="StrongPass"
coproc POP3_CONN { openssl s_client -connect "${SERVER}:${PORT}" -quiet 2>/dev/null;}
POP3_LOGIN && echo POP3_LOGIN OK
MSG_NUM=$(POP3_STAT)
#цикл перебора сообщений
while ((MSG_NUM));do
POP3_TOP $MSG_NUM 1 # Заголовки + 1 строку сообщения
# POP3_RETR $MSG_NUM # сообщения целиком
# POP3_DELE $MSG_NUM # помечаем к удалению.
((--MSG_NUM))
done
POP3_QUIT
help coproc
help read
man openssl
—
Please open Telegram to view this post
VIEW IN TELEGRAM