Как стать автором
Поиск
Написать публикацию
Обновить

Комментарии 22

Ох уж эти админы, никак свой паппет настроить не могут!

Админы тут не при чем. Максимум "devops" стажер.
Раньше любой, кто написал php скрипт и захостил его в денвере на локалхосте, считал себя программистом. Теперь смог подключиться по ssh и все, ты devops.

Некоторые переменные окружения меняются от сессии к сессии. Захотел я себе сделать автомонтирование сетевых (gvfs) дисков. Вроде, всё просто: gio mount smb://something, но… чтобы это заработало нужна установленная переменная DBUS_SESSION_BUS_ADDRESS. Выцепить её оказалось не совсем просто:
MATE_PID=$(pgrep mate-session -u $USER)
DBUS_ADDR=$(cat /proc/$MATE_PID/environ |grep -z "^DBUS_SESSION_BUS_ADDRESS=")

А симптомы были те же — всё работает из консоли, но совсем не работает из крона.

Самое время узнать про существование -z флага у grep. Спасибо!

В прошлый раз, когда мы использовали этот флаг в сочетании с -E (с -P оно даже в тот момент не работало), оно сломалось после обновления grep и после этого у нас nginx перестал отдавать большие ответы от php-fpm, если ты помнишь :).

А ещё самое время узнать о существовании флага -F (или команды fgrep) и что большинство команд командной строки умеют сами из файла читать. Строка из вашей статьи должна быть переписана так (такой поиск будет быстрее, у вас огромный лог и в нём не будут случайно срабатывать спецсимволы регулярок):

tr '\0' '\n' < /proc/10684/environ | fgrep SUDO_USER

Ну или как вам уже подсказали, так:

grep -Fz SUDO_USER /proc/10684/environ

И ещё какая-то странная идея экранировать всё подряд, да ещё и двойными кавычками. Если уж использовать кавычки «на всякий случай», надо использовать одинарные, в них интерполяции нет. Строка из комментария выше:

DBUS_ADDR=$(grep -z ^DBUS_SESSION_BUS_ADDRESS= /proc/$MATE_PID/environ)

«Бесполезная кошка» (useless cat) — антипаттерн в шеллах, не надо его использовать.
Можно пойти дальше, вместо
cat /proc/self/environ| tr '\0' '\n' | grep 'SOMETHING'

использовать
strings -a /proc/self/environ | grep 'SOMETHING'
cat действительно лишний, спасибо

А что в бесполезной кошке плохо?
Используя fgrep и чтение из файла мы делаем совсем не по unix-way, и одна программа делает не одно действие. Для написания шелл-скриптов убирать кошек верно, это и оптимизация, и выразительность. Но при работе из командной строки смысла в этом не вижу.

Unix-way — это не «одно действие», это «делать что-то одно, но хорошо», т.е. «одна функция», а количество действий необходимых для этого «одного» может быть разным. Если уж на то пошло, то cat выполняет как минимум два действия — читает и пишет, fgrep — читает и ищет, а если вы посчитаете количество «действий» в самом шелле при выполнении одной команды (а особенно если их несколько, связанных потоками)… вам должно стать страшно.

Возвращаясь к нашим баранам — (f)grep ищёт что-то в файле/потоке — и неважно откуда этот поток берется — из cat или напрямую из файла, к тому же, без операции чтения тут не обойтись в принципе. Если программа в состоянии читать файл напрямую — ничего плохого в этом нет, даже наоборот, и она всё ещё выполняет только одну функцию — поиск.

При работе из командной строки это как минимум печатать лишние символы, а вообще (к вопросу о том что плохо в бесполезной кошке) — как это ни удивительно, но это просто бесполезное дополнительное действие и неразумная трата системных ресурсов — создается дополнительный процесс, под него выделяется память, файловые дескрипторы etc — куча дополнительных накладных расходов. Да, на почти любой современной системе это практически незаметно, особенно если не выполняется 1000 раз в секунду — но — зачем?

И наконец… unix-way — это не догма, не закон и даже не правило, и совсем не отменяет здравого смысла, далеко не всегда имеет смысл сохранять философию «одна программа — одна функция», по соображениям эффективности, целостности и много ещё каким, но это тема для целой статьи…

Просто если буквально следовать этому, то вместо опций, модифицирующих поведение программ (иногда очень существенно) у нас будет одна программа на каждый вариант поведения/обработки, и придётся их комбинировать для достижения одной функции (причём не факт что позволит достичь результата за один проход) — это разве разумно?
С другой стороны, авторы find, ИМХО, все-таки зашли слишком далеко, особенно с опциями вроде -delete :)

А с третьей, когда необходимо обработать или удалить парочку сотен тысяч файлов в конкретной директории, то только find и спасает, ибо он применяет -delete (-exec) в цикле по мере нахождения файлов, а все остальные команды (типа rm dir/*, some_command dir/*, etc) вначале пытаются распаковать список аргументов, на чем благополучно зависают.

Ну почему, xargs вроде бы тоже так работает — он накапливает буфер для того количества аргументов, которые вы передали, или что-то около 5000 по умолчанию.

Стоит отметить, что зависают не сами команды, а оболочка, которая раскрывает вайлдкарды в список и именно это занимает много времени. (А бывает так что список просто слишком длинный) Сам же процесс удаления почти не отличается.
ls -l /proc/$pid/{exe,cwd} покажут сразу и «настоящий» экзешник и текущий cwd (который часто тот который был в момент запуска, хотя и не всегда), без шаманства с переменными среды.

К тому же, переменные среды могут быть не совсем верными, а ps покажет то что захочет сам процесс.

bash -l кроме того, что часто чинит env-переменные (отчего в неё любят заворачивать cron-задачи), может и ломать. Лет 5 назад, когда RHEL 7 только появился, а контейнеризация ещё не была так популярна, один заказчик выдал нам пачку виртуалок на RHEL 7 с какими-то приблудами безопасности, на которые вы водрузили разрабатываемый нами веб-портал с помощью паппетов, кривых рук и такой-то матери. В том числе в комплекте была cron-задача, запускавшаяся каждую минуту. Каждый примерно месяц (с поразительной периодичностью) виртуальные машины зависали намертво. Оказывается, что-то там (память, к сожалению, не сохранила, что именно), реагировало на каждый логин в систему и на 65536-м логине вешало всё к чёрту. Убирание bash -l обёртки из crontab'а решало проблему (точнее, делало её крайне редкой). Такая вот прохладная история.

поддерживаю, в crontab больше подходит:
SHELL=/bin/bash
BASH_ENV=$HOME/.bashrc

Уже откройте для себя systemd.timers. Там еще 100500 удобных вещей, которые можно делать без баш-портянок. И такой проблемы, как "я запустил руками — работает, а по крону — не работает" не стоит впринципе.

Как же это «не стоит впринципе»? Вот ради эксперимента попробовал на том же сервере, где собирал примеры.
alexxz@bi1.mlan:~> sudo systemd-run --on-active=1 /bin/sh -c 'env > /tmp/foo'
Running timer as unit run-r5f95073d0d5a4874832429b0b4168aa5.timer.
Will run service as unit run-r5f95073d0d5a4874832429b0b4168aa5.service.
alexxz@bi1.mlan:~> cat /tmp/foo | tr '\0' '\n'
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
SHLVL=1
LC_CTYPE=en_US.UTF-8
_=/usr/bin/env

Я вижу всё такое же неполное окружение, как из крона. Лишь только самую малость получше.

Вы немного не поняли. С systemd.timer вы настраиваете юнит, прописывая всё необходимое окружение.
После этого не имеет значение как и кем этот юнит будет запущен — вами ( systemctl start ) или планировщиком — всё будет выполнено в фиксированном, одинаковом окружении.


Более того, с кроном есть еще одна очень гадкая проблема, которая обнаружется потом, когда вы будете думать, что всё работает: например, скрипт в кроне делает какие-либо операции, создавая для себя каталоги/файлы. Вы дёрнули этот скрипт из-под другого юзера, а хуже — из-под рута, скрипт создал каталоги с соотв. владельцем. Всё работает корректно, вы радостно идёте домой. Вот только при следующем запуске ваш скрипт обломает зубки, тк прав доступа ему уже не хватит.
И этой пробемы снова нет с systemd.timer'ами. И там еще куча полезного.


Но двайте я подслащу хейтерам системд: есть там недостаток, довольно непрятный. Без костылей там нет нормальных почтовых уведомлений о проблемах с запуском юнита, как это есть в cron. Увы и ах. Однако, при всей моей любви к крону, я вижу кучу профита у timer'ов и выбираю их.

Вообще правильно Вам товарищ говорит, не нужно ничего из крона запускать, когда есть скриптовый фреймворк :). Правда проблему переменных окружения это, конечно же, не решает.
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий