#! / usr/bin / env и имена процессов: переносимость по цене?

есть много хороший причины использовать #! / usr/bin / env. Итог: это делает ваш код более переносимым. Ну, типа того. Проверить это....


у меня два почти одинаковых сценария,bintest.py

#! /usr/bin/python
import time
time.sleep(5*60)

и envtest.py

#! /usr/bin/env python
import time
time.sleep(5*60)

обратите внимание, что они отличаются только в их shebangs.


bintest.py работает, как ожидалось

br@carina:~$ ./bintest.py & ps && killall bintest.py
[1] 15061
  PID TTY          TIME CMD
14625 pts/0    00:00:00 bash
15061 pts/0    00:00:00 bintest.py
15062 pts/0    00:00:00 ps
br@carina:~$ 
[1]+  Terminated              ./bintest.py

но envtest.py делает что-то менее-чем-оптимальное

br@carina:~$ ./envtest.py & ps && killall envtest.py
[1] 15066
  PID TTY          TIME CMD
14625 pts/0    00:00:00 bash
15066 pts/0    00:00:00 python
15067 pts/0    00:00:00 ps
envtest.py: no process found
br@carina:~$ killall python
br@carina:~$ 
[1]+  Terminated              ./envtest.py

то, что мы видели, это использование #! /usr/bin/env заставил процесс получить имя "python", а не "envtest.py", таким образом переводя наш killall неэффективны. На каком-то уровне кажется, что мы обменяли один вид переносимости на другой: теперь мы можем легко поменять интерпретаторы python, но мы потеряли "способность убивать" в командной строке. Что это значит? Если здесь есть наилучшая практика для достижения обоих, что это такое?

4 ответов


"kill-ability" в командной строке смогите адресованным портативно и надежно используя PID backgrounded процесса полученного от shell $! переменной.

$ ./bintest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid
[1] 2993
bg_pid=2993
  PID TTY          TIME CMD
 2410 pts/0    00:00:00 bash
 2993 pts/0    00:00:00 bintest.py
 2994 pts/0    00:00:00 ps
$ 
[1]+  Terminated              ./bintest.py
$ 

и envtest.py

$ ./envtest.py & bg_pid=$! ; echo bg_pid=$bg_pid ; ps && kill $bg_pid
[1] 3016
bg_pid=3016
  PID TTY          TIME CMD
 2410 pts/0    00:00:00 bash
 3016 pts/0    00:00:00 python
 3017 pts/0    00:00:00 ps
$ 
[1]+  Terminated              ./envtest.py
$ 

как указывает @Adam Bryzak, ни один скрипт не заставляет заголовок процесса устанавливаться на Mac OS X. Поэтому, если эта функция является твердым требованием, вам может потребоваться установить и использовать модуль python переноса функции setproctitle С приложение.

этот пост Stackoverflow обсуждает настройка заголовка процесса в python


Я не думаю, что вы можете положиться на killall использование имени скрипта для работы все время. В Mac OS X я получаю следующий вывод из ps после выполнения обоих скриптов:

 2108 ttys004    0:00.04 /usr/local/bin/python /Users/adam/bin/bintest.py
 2133 ttys004    0:00.03 python /Users/adam/bin/envtest.py

и под управлением killall bintest.py результаты

No matching processes belonging to you were found

хотя я все равно хотел бы решение, которое делает языки сценариев как кросс-платформенными, так и простыми для мониторинга из командной строки, Если вы просто ищете альтернативу killall <scriptname> чтобы остановить пользовательские службы, вот как я решил это:

kill `ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*\([0-9]*\).*$//gp'`

для тех, кто не слишком хорошо знаком с ps и regexes,ps ' s -f модификатор имеет список "полный" набор информации о процессе, включая его аргументы командной строки, и -C говорит ему фильтровать список только команды, соответствующие следующему аргументу командной строки. Заменить <interpreterName> С python или node или что-то еще.

sed ' s -n аргумент говорит ему ничего не печатать по умолчанию, и скрипт regex должен явно указать, что вы хотите что-то напечатать.

в регулярном выражении, первый /<scriptName>/ указывает фильтровать результаты только по строкам, содержащим внутреннее регулярное выражение. Вы можете заменить <scriptName> С envtest, например.

в s указывает, что будет следовать регулярное выражение подстановки. /^[^0-9]*\([0-9]*\).*$/ быть частью соответствия линии и // быть частью замены. В соответствующей части строки ^ в начале и $ в самом конце означает, что матч должен начинаться с начала строки и заканчиваться в конце строки-вся проверяемая строка должна быть заменена.

на [^0-9]* включает в себя несколько вещей: [] используются для определения набора допустимых письмена. В этой части регулярного выражения тире - - означает диапазон символов, так что он расширяется до 0123456789. The ^ здесь означает "не" и сразу означает "соответствовать любому символу, который не является числом". Звездочка * после этого означает, что он будет продолжать совпадать с символами в этом наборе, пока не встретит несоответствующий символ, в данном случае число.

на \([0-9]*\) имеет две части,\(\) и [0-9]*. Последнее должно быть легко следовать из предыдущее объяснение: он соответствует только числам и захватывает столько, сколько может. The \(\) означает сохранение содержимого того, что соответствует временной переменной. (В других версиях регулярных выражений, включая Javascript и Perl,() используется, вместо этого.)

наконец,.* значит Матч всех остальных символов, как . означает любой возможный характер.

на // часть говорит, чтобы заменить согласованную часть линии (которая является всей линией в этом случай) с , который является ссылкой на сохраненную временную переменную (если бы было два \(\) разделы, первый в регулярном выражении будет и второй ).

на g после этого означает быть "жадным" и запускать этот соответствующий код на каждой встреченной строке, а p означает печать любой строки, которая достигла этой точки.

технически, это взорвется, если у вас есть несколько копий вашего сценария, и вы действительно хочется немного потяжелее:

ps -fC <interpreterName> | sed -n '/<scriptName>/s/^[^0-9]*\([0-9]*\).$/kill /gp' | bash

если вы хотите действительно реплицировать функциональность kill*all*, но это порождает отдельную оболочку bash для каждого сценария, который вы хотите убить.


в комментарии вы говорите, что проблема в том, что разные системы (особенно MacOS и Linux) размещают исполняемые файлы в разных каталогах.

вы можете обойти это, создав каталог с полным путем на обе системы, и создавая символические ссылки на исполняемые файлы.

эксперимент на Ubuntu, Solaris и Cygwin указывает, что исполняемый файл с именем в shebang может быть символической ссылкой. (У меня нет доступа к системе MacOS, поэтому я не уверен, что там это сработает.)

например, в моей системе Ubuntu:

$ cat hello.bash
#!/tmp/bin/bash

echo Yes, it works
$ ./hello.bash
-bash: ./hello.bash: /tmp/bin/bash: bad interpreter: Permission denied
$ mkdir /tmp/bin
$ ln -s /bin/bash /tmp/bin/.
$ ./hello.bash
Yes, it works
$ 

настройка общего каталога на всех соответствующих системах, по общему признанию, неудобно. (Я использовал /tmp для этого примера; другое место может быть лучше.)

Я не уверен, как это будет взаимодействовать с killall, но стоит попробовать.