Как «правильно» запустить приложение из оболочки

21

Мне сложно точно сформулировать вопрос, но я сделаю все возможное. Я использую в dwmкачестве моего оконного менеджера по умолчанию иdmenuкак мой запуск приложения. Я почти не использую приложения с графическим интерфейсом, кроме своего браузера. Большая часть моей работы выполняется непосредственно из командной строки. Кроме того, я большой поклонник минимализма в отношении операционных систем, приложений и т. Д. Одним из инструментов, от которого я никогда не избавлялся, была программа запуска приложений. Главным образом потому, что мне не хватает четкого понимания того, как работают программы запуска приложений / что они делают. Даже обширный поиск в Интернете показывает только смутное объяснение. То, что я хочу сделать, - это избавиться даже от моего средства запуска приложений, потому что кроме фактического запуска приложения, я его абсолютно не использую. Для этого мне бы очень хотелось узнать, как «правильно» запускать приложения из оболочки. При этом значение «правильно» может быть аппроксимировано «как бы запускала приложение».

Я знаю о следующих способах порождения процессов из оболочки:

  1. exec /path/to/Program заменить оболочку указанной командой без создания нового процесса
  2. sh -c /path/to/Program запустить процесс, зависящий от оболочки
  3. /path/to/Program запустить процесс, зависящий от оболочки
  4. /path/to/Program 2>&1 & запустить оболочку независимый процесс
  5. nohup /path/to/Program & запустить процесс, независимый от оболочки, и перенаправить вывод в nohup.out

Обновление 1: я могу проиллюстрировать, что, например dmenu, восстанавливает его из повторных вызовов в ps -eflразличных условиях. Он порождает новую оболочку /bin/bashи, как потомок этой оболочки, приложение /path/to/Program. Пока ребенок вокруг, пока вокруг будет раковина. (Как это управляет, я не знаю ...) В отличие от этого, если вы nohup /path/to/Program &выполняете /bin/bashкоманду из оболочки, тогда программа станет дочерней для этой оболочки, НО, если вы выйдете из этой оболочки, родительский процесс программы будет самым верхним процессом. Таким образом, если первый процесс был, например, /sbin/init verboseи имеет, PPID 1то он будет родителем программы. Вот что я пытался объяснить с помощью графика: chromiumбыл запущен с помощью dmenu, firefoxбыл запущен с помощью exec firefox & exit:

systemd-+-acpid
        |-bash---chromium-+-chrome-sandbox---chromium-+-chrome-sandbox---nacl_helper
        |                 |                           `-chromium---5*[chromium-+-{Chrome_ChildIOT}]
        |                 |                                                    |-{Compositor}]
        |                 |                                                    |-{HTMLParserThrea}]
        |                 |                                                    |-{OptimizingCompi}]
        |                 |                                                    `-3*[{v8:SweeperThrea}]]
        |                 |-chromium
        |                 |-chromium-+-chromium
        |                 |          |-{Chrome_ChildIOT}
        |                 |          `-{Watchdog}
        |                 |-{AudioThread}
        |                 |-3*[{BrowserBlocking}]
        |                 |-{BrowserWatchdog}
        |                 |-5*[{CachePoolWorker}]
        |                 |-{Chrome_CacheThr}
        |                 |-{Chrome_DBThread}
        |                 |-{Chrome_FileThre}
        |                 |-{Chrome_FileUser}
        |                 |-{Chrome_HistoryT}
        |                 |-{Chrome_IOThread}
        |                 |-{Chrome_ProcessL}
        |                 |-{Chrome_SafeBrow}
        |                 |-{CrShutdownDetec}
        |                 |-{IndexedDB}
        |                 |-{LevelDBEnv}
        |                 |-{NSS SSL ThreadW}
        |                 |-{NetworkChangeNo}
        |                 |-2*[{Proxy resolver}]
        |                 |-{WorkerPool/1201}
        |                 |-{WorkerPool/2059}
        |                 |-{WorkerPool/2579}
        |                 |-{WorkerPool/2590}
        |                 |-{WorkerPool/2592}
        |                 |-{WorkerPool/2608}
        |                 |-{WorkerPool/2973}
        |                 |-{WorkerPool/2974}
        |                 |-{chromium}
        |                 |-{extension_crash}
        |                 |-{gpu-process_cra}
        |                 |-{handle-watcher-}
        |                 |-{inotify_reader}
        |                 |-{ppapi_crash_upl}
        |                 `-{renderer_crash_}
        |-2*[dbus-daemon]
        |-dbus-launch
        |-dhcpcd
        |-firefox-+-4*[{Analysis Helper}]
        |         |-{Cache I/O}
        |         |-{Cache2 I/O}
        |         |-{Cert Verify}
        |         |-3*[{DOM Worker}]
        |         |-{Gecko_IOThread}
        |         |-{HTML5 Parser}
        |         |-{Hang Monitor}
        |         |-{Image Scaler}
        |         |-{JS GC Helper}
        |         |-{JS Watchdog}
        |         |-{Proxy R~olution}
        |         |-{Socket Thread}
        |         |-{Timer}
        |         |-{URL Classifier}
        |         |-{gmain}
        |         |-{localStorage DB}
        |         |-{mozStorage #1}
        |         |-{mozStorage #2}
        |         |-{mozStorage #3}
        |         |-{mozStorage #4}
        |         `-{mozStorage #5}
        |-gpg-agent
        |-login---bash---startx---xinit-+-Xorg.bin-+-xf86-video-inte
        |                               |          `-{Xorg.bin}
        |                               `-dwm-+-dwmstatus
        |                                     `-xterm---bash-+-bash
        |                                                    `-pstree
        |-systemd---(sd-pam)
        |-systemd-journal
        |-systemd-logind
        |-systemd-udevd
        |-wpa_actiond
        `-wpa_supplicant

Обновление 2: я думаю, что вопрос также можно свести к следующему: что должно быть родителем процесса? Должно ли это быть, например, оболочкой или это initпроцесс, то есть процесс с PID 1?

lord.garbage
источник
3
Краткий ответ на ваш вопрос будет «все, что дает результаты, которые вы хотите».
Уэйн Вернер
1
черт, мусор - ты задаешь несколько хороших вопросов. но я думаю, что Уэйн здесь на носу, - спрашивает твое последнее редактирование init- на что ответ может быть ... может быть? это зависит от того, как / если вы планируете поговорить с ним, что initвы используете и где находятся каналы передачи данных. В общем, эти вещи, как правило, работают само собой - вот для чего init. В любом случае, обычно, когда вы демонизируете процесс init. Или, если вы хотите контроль работы, текущая оболочка.
mikeserv
Хахаха, ура @mikeserv; 4:37 утра здесь и уже первый смех дня. Правда, эта штука всегда как-то срабатывает. Я удалю dmenuи посмотрю, как я справляюсь с тем, что узнал. Я нахожу exec /path/to/Program & exitили /bin/bash -c /path/to/Program & exitбыть вполне пригодным для использования. Но все они делают 1то initродителем из Programкоторых хорошо со мной до тех пор , как это имеет смысл , и не нарушает основные *nixпринципы.
lord.garbage
@ lord.garbage - это потому что ты exec &, я думаю. Я обычно просто делаю свои вещи из терминала ... может быть, вы бы получили некоторую пользу от вопроса Бена Кроуэлла здесь . У меня есть ответ, но все они очень хорошие. в любом случае, когда вы создаете фоновый процесс и его родитель умирает, как: sh -c 'cat & kill $$'вы осиротите его, и в конечном итоге он будет пожинен. это работа инициата - вот почему они все к ней относятся.
mikeserv
Может быть, проще вопрос сейчас: как это возможно , чтобы получить вышеуказанное дерево процессов из оболочки: systemd--bash--chromium. Все методы, которые я пробую, в конечном итоге приводят к дереву процессов следующей формы, systemd--chromiumкогда я запускаю firefox из оболочки. Как оболочка демонизируется здесь? Он не связан ни с одним терминалом.
lord.garbage

Ответы:

7

Ну, у тебя, кажется, есть хорошее понимание этого. Чтобы уточнить, что у вас есть,

  • sh -c /path/to/Program довольно похож на

    $ sh 
    % / path / to / Program 
    % Ctrl+ D                             (или вы можете ввести « выход ») 
    $

    где вы запускаете новый процесс оболочки, задаете путь команды приложения к новой оболочке, а затем позволяете новой оболочке завершиться. Я показал новую оболочку, дающую другое приглашение в целях иллюстрации; это, вероятно, не произойдет в реальной жизни. Конструкция в основном используется для выполнения хитрых вещей, как упаковка несколько команд в одной связку, так что они выглядят как единая команда (вроде одноразового безымянного сценария), или здания , сложные команд, возможно , от оболочки переменных. Вы вряд ли когда-либо будете использовать его только для запуска одной программы с простыми аргументами.sh -c "command"

  • 2>&1означает перенаправление стандартной ошибки на стандартный вывод. Это на самом деле не имеет ничего общего с &; скорее, вы используете его, когда команда отправляет сообщения об ошибках на экран, даже если вы говорите и хотите записать сообщения об ошибках в файл.command > file
  • Перенаправление вывода на nohup.outявляется тривиальным побочным эффектом nohup. Основная цель состоит в том, чтобы запускаться асинхронно (обычно известный как «в фоновом режиме» или как «процесс, не зависящий от оболочки», чтобы использовать ваши слова) и настраивать его так, чтобы он имел больше шансов продолжить работу, если вы завершить работу оболочки (например, выйти из системы), пока команда еще выполняется.nohup command &command

bash(1)и Справочное руководство Bash являются хорошими источниками информации.

G-Man говорит: «Восстанови Монику»
источник
7

Есть несколько способов выполнить программу и отсоединить ее от терминала. Один из них - запустить его в фоновом режиме подоболочки (замените его firefoxна свою любимую программу):

(firefox &)

Другой способ отказаться от процесса:

firefox & disown firefox

Если вы хотите знать , как приложение пусковые работы, dmenuобеспечивает 1 двоичный и 2 сценарии оболочки: dmenu, dmenu_pathи dmenu_run, соответственно.

dmenu_rundmenu_pathнаправляет вывод в dmenu, которое, в свою очередь, направляет в любую вашу $SHELLпеременную. Если он пуст, он будет использовать /bin/sh.

#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

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

#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
        cache=$cachedir/dmenu_run
else
        cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
IFS=:
if stest -dqr -n "$cache" $PATH; then
        stest -flx $PATH | sort -u | tee "$cache"
else
        cat "$cache"
fi

Нет необходимости запускать программы в оболочках. Другой способ написать dmenu_runбез использования оболочки:

#!/bin/sh
$(dmenu_path | dmenu "$@") &
svenper
источник
6

Мне очень нравится ответ G-Man. Но я отвечаю, потому что я думаю, что вы сбиваете с толку проблемы. Как указывает Уэйн, лучший ответ - «все, что дает желаемый результат».

В управлении процессами Unix у каждого процесса есть родитель. Единственным исключением является initпроцесс, который запускается ОС при загрузке. Это нормальное поведение для родительского процесса, чтобы взять все его дочерние процессы с собой, когда он умирает. Это делается путем отправки сигнала SIGHUP всем дочерним процессам; обработка SIGHUP по умолчанию завершает процесс.

Порождение пользовательских процессов в оболочке ничем не отличается от того, что вы кодировали вызовы fork (2) / exec (3) на выбранном вами языке. Оболочка является вашим родителем, и если оболочка завершается (например, вы выходите из системы), то дочерний процесс (-ы), который она порождает, идет вместе с ним. Нюансы, которые вы описываете, являются просто способами изменения этого поведения.

exec /path/to/programэто как вызов exec (3) . Да, он заменит вашу оболочку program, оставив родительский элемент, запустивший оболочку.

sh -c /path/to/programвид бессмысленно создает дочерний процесс оболочки, который будет создавать дочерний процесс program. Это полезно только в том случае, если /path/to/programна самом деле это последовательность инструкций скрипта, а не исполняемый файл. ( sh /path/to/script.shможет использоваться для запуска сценария оболочки, который не имеет разрешений на выполнение в подчиненной оболочке)

/path/to/programсоздает процесс «переднего плана», означающий, что оболочка ожидает завершения процесса, прежде чем предпринимать какие-либо другие действия. В контексте системного вызова это похоже на fork (2) / exec (3) / waitpid (2) . Обратите внимание, что ребенок наследует stdin / stdout / stderr от родителя.

/path/to/program &(игнорируя перенаправление) создает «фоновый процесс». Процесс по-прежнему является дочерним элементом оболочки, но родитель не ожидает его завершения.

nohup /path/to/programвызывает nohup (1) для предотвращения отправки SIGHUP, programесли управляющий терминал закрыт. Находится ли он на переднем плане или на заднем плане - выбор (хотя чаще всего этот процесс основан на заднем плане). Обратите внимание, что nohup.outэто только вывод, если вы не перенаправите иначе stdout.

Когда вы помещаете процесс в фоновый режим, если родительский процесс умирает, происходит одно из двух. Если родитель является управляющим терминалом , то SIGHUP будет отправлено детям. Если это не так, то процесс может быть «осиротевшим» и наследоваться initпроцессом.

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

Учитывая все вышесказанное, я не думаю, что вам следует беспокоиться о создании процессов с помощью оболочки, вложенных оболочек или подпроцессов, если только вы не столкнулись с конкретной проблемой, связанной с управлением процессами.

JWM
источник
nitpick: sh -c /path/to/programне будет запускать программу как скрипт оболочки, если в ней отсутствуют исполняемые биты, sh /path/to/programбудет. sh -c /path/to/programпросто откроет оболочку и запустит /path/to/programкоманду в этой оболочке, что приведет к ошибке, если она не исполняется.
filbranden
Ну, если мы придираемся, мы оба не правы. sh -c /path/to/programчитает команды из /path/to/programввода в оболочку. Это не требует, чтобы файл имел разрешение на выполнение, но это должен быть сценарий оболочки
jwm
Хм, sh /path/to/programделает это. Просто сам попробовал:, echo echo hello world >abc.shпотом sh ./abc.shпечатает hello world, пока sh -c ./abc.shговорит sh: ./abc.sh: Permission denied(что то же самое, как если бы вы запускали ./abc.shв текущей оболочке напрямую.) Я что-то пропустил? (Или, может быть, я не выразил себя хорошо в предыдущем комментарии ...)
filbranden
Моя вина. sh -c _something_это то же самое, что просто набирать _something_в командной строке, за исключением порождения нижней оболочки. Таким образом, вы правы, что произойдет сбой, если пропустить бит выполнения (в виде файла). С другой стороны, вы можете предоставить ряд команд оболочки, например, sh -c "echo hello world"и все будет работать отлично. Поэтому не требуется, чтобы то, что вы вводите, имело бит выполнения (или даже файл), только то, что интерпретатор оболочки может что-то с ним сделать.
JWM
Я полагаю, что OP имел в виду какой-то скомпилированный или системный исполняемый файл, поэтому было принято разрешение на выполнение.
JWM