Cómo "correctamente" iniciar una aplicación desde un shell


21

Me resulta difícil formular la pregunta con precisión, pero daré lo mejor de mí. Yo uso dwmcomo mi administrador de ventanas predeterminado ydmenucomo mi lanzador de aplicaciones. Apenas uso aplicaciones GUI aparte de mi navegador. La mayor parte de mi trabajo se realiza directamente desde la línea de comandos. Además, soy un gran admirador del minimalismo con respecto a los sistemas operativos, aplicaciones, etc. Una de las herramientas de las que nunca me deshice fue un lanzador de aplicaciones. Principalmente porque no tengo una comprensión precisa de cómo funcionan los lanzadores de aplicaciones / qué hacen. Incluso la búsqueda exhaustiva en Internet solo muestra una vaga explicación. Lo que quiero hacer es deshacerme incluso de mi iniciador de aplicaciones porque, aparte de engendrar la aplicación, no tengo absolutamente ninguna utilidad. Para hacer esto, me gustaría saber cómo iniciar "correctamente" las aplicaciones desde el shell. Por lo que el significado de "correctamente" se puede aproximar por "como lo haría un lanzador de aplicaciones".

Conozco las siguientes formas de generar procesos desde el shell:

  1. exec /path/to/Program reemplazar shell con el comando especificado sin crear un nuevo proceso
  2. sh -c /path/to/Program iniciar proceso dependiente de shell
  3. /path/to/Program iniciar proceso dependiente de shell
  4. /path/to/Program 2>&1 & lanzar proceso independiente de shell
  5. nohup /path/to/Program & lanzar un proceso independiente de shell y redirigir la salida nohup.out

Actualización 1: puedo ilustrar lo que dmenuhace, por ejemplo , reconstruirlo desde llamadas repetidas a ps -efldiferentes condiciones. Genera un nuevo shell /bin/bashy, como hijo de este shell, la aplicación /path/to/Program. Mientras el niño esté cerca, la concha estará alrededor. (La forma en que maneja esto está más allá de mí ...) Por el contrario, si emite nohup /path/to/Program &un shell /bin/bash, el programa se convertirá en el hijo de este shell, PERO si sale de este shell, el padre del programa será el proceso superior. Entonces, si el primer proceso fue, por ejemplo, /sbin/init verbosey lo ha sido PPID 1, será el padre del programa. Esto es lo que intenté explicar usando un gráfico: chromiumse lanzó a través de dmenu, firefoxse lanzó usando 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

Actualización 2: Supongo que la pregunta también se puede resumir en: ¿Cuál debería ser el padre de un proceso? ¿Debería ser, por ejemplo, un shell o debería ser el initproceso, es decir, el proceso con PID 1?


3
La respuesta concisa a su pregunta sería "lo que obtenga los resultados que desea".
Wayne Werner

1
Maldita sea, basura - haces algunas buenas preguntas. pero creo que Wayne está en la nariz aquí, tu última edición pregunta init, a lo que la respuesta podría ser ... ¿tal vez? depende de cómo / si planeas hablar con él, qué initusas y dónde están los canales de datos. En general, esas cosas tenderán a resolverse por sí solas, para eso initestán. En cualquier caso, generalmente cuando demoniza un proceso entonces init. O si quieres control de trabajo, shell actual.
mikeserv

Jajaja, saludos @mikeserv; 4:37 am de la mañana aquí y ya la primera risa del día. Es cierto que esas cosas siempre funcionan de alguna manera. Eliminaré dmenuy veré cómo me llevo con lo que aprendí. Me parece exec /path/to/Program & exito /bin/bash -c /path/to/Program & exitpara ser bastante utilizable. Pero todos ellos son, 1es decir, initel padre de lo Programque está bien para mí siempre que tenga sentido y no viole ningún *nixprincipio básico .
lord.garbage

@ lord.garbage - eso es porque tú exec &, creo. Por lo general, hago mis cosas desde la terminal ... tal vez te sirva de algo la pregunta de Ben Crowell aquí . Tengo una respuesta allí, pero todas son muy buenas. de todos modos, cuando creas un proceso en segundo plano y su padre muere como: sh -c 'cat & kill $$'lo huérfano, y finalmente termina siendo cosechado. ese es el trabajo de init, por eso todos caen en él.
mikeserv

Tal vez una pregunta más simple por ahora es: ¿Cómo es posible para obtener el árbol de procesos arriba del shell: systemd--bash--chromium. Todos los métodos que pruebo en última instancia conducirán a un árbol de procesos de la siguiente forma systemd--chromiumcuando engendro firefox desde el shell. ¿Cómo se demoniza el caparazón aquí? No está asociado a ningún terminal.
lord.garbage

Respuestas:


7

Bueno, parece que lo entiendes bastante bien. Para aclarar algo de lo que tienes,

  • sh -c /path/to/Program es bastante similar a

    $ sh 
    % / ruta / a / Programa 
    % Ctrl+ D                             (o podría escribir " salir ") 
    $

    donde inicia un nuevo proceso de shell, proporcione la ruta de comando de la aplicación al nuevo shell y luego deje que el nuevo shell finalice. He mostrado el nuevo shell dando un mensaje diferente con fines ilustrativos; Esto probablemente no sucedería en la vida real. La construcción es principalmente útil para hacer cosas difíciles, como envolver múltiples comandos en un paquete, por lo que se ven como un solo comando (una especie de script sin nombre de un solo uso), o construir comandos complicados, posiblemente a partir de variables de shell. Casi nunca lo usaría solo para ejecutar un solo programa con argumento (s) simple (s).sh -c "command"

  • 2>&1significa redirigir el error estándar a la salida estándar. Esto realmente no tiene mucho que ver con &; más bien, lo usa cuando un comando envía mensajes de error a la pantalla incluso si dice y desea capturar los mensajes de error en el archivo.command > file
  • Redirigir la salida a nohup.outes un efecto secundario trivial de nohup. El propósito principal de es ejecutar de forma asincrónica (comúnmente conocido como "en segundo plano", o como un "proceso independiente de la shell", para usar sus palabras) y configurarlo para que tenga una mejor oportunidad de poder continuar ejecutándose si usted terminar el shell (por ejemplo, cerrar sesión) mientras el comando aún se está ejecutando.nohup command &command

bash(1)y el Manual de referencia de Bash son buenas fuentes de información.


7

Hay varias formas de ejecutar un programa y desconectarlo de una terminal. Una es ejecutarla en el fondo de una subshell , como esta (reemplace firefoxcon su programa favorito):

(firefox &)

Otra es rechazar el proceso:

firefox & disown firefox

Si tiene curiosidad sobre cómo funcionan los lanzadores de aplicaciones, dmenuproporciona 1 script binario y 2 scripts de shell dmenu, dmenu_pathy dmenu_run, respectivamente.

dmenu_runcanaliza la salida de dmenu_pathen dmenu, que a su vez canaliza en cualquier $SHELLvariable establecida en. Si está vacío, lo usará /bin/sh.

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

dmenu_pathes un poco más complejo, pero en resumen, proporciona una lista de binarios en la $PATHvariable de entorno y, si es posible, utiliza un caché.

#!/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

No es necesario tener programas ejecutándose en shells. Otra forma de escribir dmenu_run, sin conectarlo a un shell sería:

#!/bin/sh
$(dmenu_path | dmenu "$@") &

6

Me gusta mucho la respuesta de G-Man. Pero estoy respondiendo porque creo que tienes preocupaciones confusas. Como Wayne señala, la mejor respuesta es "lo que sea que obtenga los resultados que desea".

En la gestión de procesos de Unix, cada proceso tiene un padre. La única excepción a esto es el initproceso que inicia el sistema operativo en el arranque. Es un comportamiento normal que un proceso padre se lleve todos sus procesos hijos cuando muere. Esto se realiza enviando la señal SIGHUP a todos los procesos secundarios; el manejo predeterminado de SIGHUP termina el proceso.

La generación de shell de los procesos del usuario no es diferente de si codificara las llamadas fork (2) / exec (3) en el idioma que elija. El shell es su padre, y si el shell termina (por ejemplo, cierra la sesión), entonces el proceso o procesos secundarios que genera van con él. Los matices que describe son solo formas de modificar ese comportamiento.

exec /path/to/programes como llamar a exec (3) . Sí, reemplazará su shell con program, manteniendo cualquier padre que haya lanzado el shell.

sh -c /path/to/programcrea un proceso secundario de shell que creará un proceso secundario de program. Solo es valioso si en /path/to/programrealidad es una secuencia de instrucciones de script y no un archivo ejecutable. ( sh /path/to/script.shse puede usar para ejecutar un script de shell que carece de permisos de ejecución en un shell inferior)

/path/to/programcrea un proceso de "primer plano", lo que significa que el shell espera a que se complete el proceso antes de realizar cualquier otra acción. En el contexto de la llamada al sistema, es como fork (2) / exec (3) / waitpid (2) . Tenga en cuenta que el hijo hereda stdin / stdout / stderr del padre.

/path/to/program &(ignorando la redirección) crea un "proceso en segundo plano". El proceso todavía es un elemento secundario del shell, pero el padre no está esperando que termine.

nohup /path/to/programinvoca nohup (1) para evitar el envío de SIGHUP a programsi el terminal de control está cerrado. Si es en primer plano o en segundo plano es una opción (aunque lo más común es que el proceso sea de fondo). Tenga en cuenta que nohup.outsolo es la salida si no redirige stdout.

Cuando pone un proceso en segundo plano, si el proceso padre muere, ocurre una de dos cosas. Si el padre es un terminal de control , SIGHUP se enviará a los niños. Si no es así, el proceso puede quedar "huérfano" y el initproceso lo hereda .

Cuando redirige la entrada / salida / error, simplemente está conectando los descriptores de archivo que cada proceso tiene a archivos diferentes de los que hereda de su padre. Nada de esto afecta la propiedad del proceso o la profundidad del árbol (pero siempre tiene sentido redirigir los 3 lejos de una terminal para procesos en segundo plano).

Dicho todo esto, no creo que deba preocuparse por la creación de procesos por parte del shell o sub-shells o subprocesos, a menos que haya un problema específico que esté abordando en relación con la gestión de procesos.


nitpick: sh -c /path/to/programno ejecutará el programa como un script de shell si faltan los bits ejecutables, lo sh /path/to/programhará. sh -c /path/to/programsolo abrirá un shell y se ejecutará /path/to/programcomo un comando en ese shell, que fallará si no es ejecutable.
filbranden

Bueno, si somos quisquillosos, ambos estamos equivocados. sh -c /path/to/programlee comandos desde /path/to/programcomo entrada al shell. No requiere que el archivo tenga permiso de ejecución, pero debe ser un script de shell
jwm

Hmmm, sh /path/to/programhace eso. Solo lo intenté yo mismo echo echo hello world >abc.sh, luego sh ./abc.shimprimo hello world, mientras sh -c ./abc.shdice sh: ./abc.sh: Permission denied(que es lo mismo que si corrieras ./abc.shdirectamente en el shell actual). ¿Me perdí algo? (O tal vez no me
expresé

Mi culpa. sh -c _something_es lo mismo que escribir _something_en el símbolo del sistema, excepto por el engendro del shell inferior. Entonces tiene razón en que fallará si falta el bit de ejecución (como un archivo). Por otro lado, puede proporcionar una serie de comandos de shell como sh -c "echo hello world"y funcionará bien. Por lo tanto, no requiere que lo que escriba tenga el bit de ejecución (o incluso sea un archivo), solo que el intérprete de shell pueda hacer algo con él.
jwm

Creo que el OP se refería a algún compilado o ejecutable del sistema, por lo que se asumió el permiso de ejecución.
jwm
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.