Pull to refresh

Comments 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'ов и выбираю их.

Вообще правильно Вам товарищ говорит, не нужно ничего из крона запускать, когда есть скриптовый фреймворк :). Правда проблему переменных окружения это, конечно же, не решает.
UFO just landed and posted this here
Sign up to leave a comment.