
Tengo un script bash que inicia un script python3 (llamémoslo startup.sh
), con la línea clave:
nohup python3 -u <script> &
Cuando ssh
entro directamente y llamo a este script, el script de Python continúa ejecutándose en segundo plano después de salir. Sin embargo, cuando ejecuto esto:
ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
El proceso finaliza en cuanto ssh
termina de ejecutarlo y cierra la sesión.
¿Cuál es la diferencia entre los dos?
EDITAR: el script de Python ejecuta un servicio web a través de Bottle.
EDIT2: También lo intentécreando un script de inicioeso llama startup.sh
y ejecuta ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "sudo service start <servicename>"
, pero tiene el mismo comportamiento.
EDITAR3: Tal vez sea algo más en el guión. Aquí está la mayor parte del guión:
chmod 700 ${key_loc}
echo "INFO: Syncing files."
rsync -azP -e "ssh -i ${key_loc} -o StrictHostKeyChecking=no" ${source_client_loc} ${remote_user}@${remote_hostname}:${destination_client_loc}
echo "INFO: Running startup script."
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart"
EDITAR4: Cuando ejecuto la última línea con un sueño al final:
ssh -i ${key_loc} -o StrictHostKeyChecking=no ${remote_user}@${remote_hostname} "cd ${destination_client_loc}; chmod u+x ${ctl_script}; ./${ctl_script} restart; sleep 1"
echo "Finished"
Nunca llega echo "Finished"
y veo el mensaje del servidor Bottle, que nunca antes había visto:
Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.
Veo "Terminado" si ingreso SSH manualmente y finalizo el proceso yo mismo.
EDIT5: Al usar EDIT4, si hago una solicitud a cualquier punto final, obtengo una página de regreso, pero la botella genera errores:
Bottle vx.x.x server starting up (using WSGIRefServer())...
Listening on <URL>
Hit Ctrl-C to quit.
----------------------------------------
Exception happened during processing of request from ('<IP>', 55104)
Respuesta1
Desconectaría el comando de sus flujos de error y de entrada/salida estándar:
nohup python3 -u <script> </dev/null >/dev/null 2>&1 &
ssh
necesita un indicador que no tenga más salida y que no requiera más entrada. Tener algo más como entrada y redirigir la salida significa que ssh
se puede salir de forma segura, ya que la entrada/salida no proviene ni va al terminal. Esto significa que la entrada debe provenir de otro lugar y la salida (tanto STDOUT como STDERR) debe ir a otro lugar.
La </dev/null
pieza se especifica /dev/null
como entrada para <script>
. Por qué eso es útil aquí:
Redirigir /dev/null a stdin dará un EOF inmediato a cualquier llamada de lectura de ese proceso. Esto suele ser útil para separar un proceso de un tty (dicho proceso se denomina demonio). Por ejemplo, al iniciar un proceso en segundo plano de forma remota a través de ssh, debe redirigir la entrada estándar para evitar que el proceso espere una entrada local. https://stackoverflow.com/questions/19955260/what-is-dev-null-in-bash/19955475#19955475
Alternativamente, la redirección desde otra fuente de entrada debería ser relativamente segura siempre y cuando ssh
no sea necesario mantener abierta la sesión actual.
Con la >/dev/null
parte, el shell redirige la salida estándar a /dev/null esencialmente descartándola. >/path/to/file
también funcionará.
La última parte 2>&1
es redirigir STDERR a STDOUT.
Hay tres fuentes estándar de entrada y salida para un programa. La entrada estándar generalmente proviene del teclado si es un programa interactivo, o de otro programa si está procesando la salida del otro programa. El programa normalmente imprime en salida estándar y, a veces, imprime en error estándar. Estos tres descriptores de archivos (puede considerarlos como "canalizaciones de datos") a menudo se denominan STDIN, STDOUT y STDERR.
¡A veces no tienen nombre, están numerados! Las numeraciones integradas para ellos son 0, 1 y 2, en ese orden. De forma predeterminada, si no nombras o numeras uno explícitamente, estás hablando de STDOUT.
Dado ese contexto, puede ver que el comando anterior redirige la salida estándar a /dev/null, que es un lugar donde puede volcar cualquier cosa que no desee (a menudo llamado bit-bucket), y luego redirige el error estándar a la salida estándar ( tienes que poner un & delante del destino cuando hagas esto).
Por lo tanto, la explicación breve es que “todos los resultados de este comando deberían arrojarse a un agujero negro”. ¡Esa es una buena manera de hacer que un programa sea realmente silencioso!
¿Qué significa > /dev/null 2>&1? | xaprb
Respuesta2
Mira a man ssh
:
ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname [command]
Cuando ejecuta, ssh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> "./startup.sh"
está ejecutando el script de shell startup.sh como un comando ssh.
De la descripción:
Si se especifica el comando, se ejecuta en el host remoto en lugar de en un shell de inicio de sesión.
En base a esto, debería ejecutar el script de forma remota.
La diferencia entre eso y ejecutarlo nohup python3 -u <script> &
en su terminal local es que este se ejecuta como un proceso en segundo plano local mientras que el comando ssh intenta ejecutarlo como un proceso en segundo plano remoto.
Si desea ejecutar el script localmente, no ejecute startup.sh como parte del comando ssh. Podrías intentar algo comossh -i <keyfile> -o StrictHostKeyChecking=no <user>@<hostname> && "./startup.sh"
Si su intención es ejecutar el script de forma remota y desea que este proceso continúe después de que finalice su sesión ssh, primero deberá iniciar una screen
sesión en el host remoto. Luego debe ejecutar el script de Python dentro de la pantalla y continuará ejecutándose después de que finalice su sesión ssh.
VerManual de usuario de la pantalla
Si bien creo que la pantalla es su mejor opción, si debe usar nohup, considere configurarlo shopt -s huponexit
en el host remoto antes de ejecutar el comando nohup. Alternativamente, puede utilizar disown -h [jobID]
para marcar el proceso para que no se le envíe SIGHUP.1
¿Cómo sigo ejecutando el trabajo después de salir del indicador de shell en segundo plano?
Su sistema utiliza la señal SIGHUP (colgar) en el terminal de control o en la muerte del proceso de control. Puede usar SIGHUP para recargar archivos de configuración y abrir/cerrar archivos de registro también. En otras palabras, si cierra sesión en su terminal, todos los trabajos en ejecución finalizarán. Para evitar esto, puede pasar la opción -h para rechazar el comando. Esta opción marca cada ID de trabajo para que SIGHUP no se envíe al trabajo si el shell recibe un SIGHUP.
Además, consulte este resumen de cómo huponexit
funciona cuando se sale, se cierra o se suelta un shell. Supongo que su problema actual está relacionado con cómo finaliza la sesión de Shell.2
Todos los procesos secundarios, en segundo plano o no, de un shell abierto a través de una conexión ssh se eliminan con SIGHUP cuando la conexión ssh se cierra solo si la opción huponexit está configurada: ejecute shopt huponexit para ver si esto es cierto.
Si huponexit es verdadero, entonces puede usar nohup o disown para disociar el proceso del shell para que no se elimine cuando salga. O ejecute cosas con pantalla.
Si huponexit es falso, que es el valor predeterminado en al menos algunos Linux hoy en día, entonces los trabajos en segundo plano no se eliminarán al cerrar sesión normalmente.
- Pero incluso si huponexit es falso, si la conexión ssh se interrumpe o se interrumpe (a diferencia del cierre de sesión normal), los procesos en segundo plano aún se eliminarán. Esto se puede evitar rechazando o nohup como en (2).
Finalmente, aquí hay algunos ejemplos de cómo usar shopt huponexit.3
$ shopt -s huponexit; shopt | grep huponexit
huponexit on
# Background jobs will be terminated with SIGHUP when shell exits
$ shopt -u huponexit; shopt | grep huponexit
huponexit off
# Background jobs will NOT be terminated with SIGHUP when shell exits
Respuesta3
¿Quizás valga la pena probar -n
la opción al iniciar un ssh
? Evitará la dependencia del proceso remoto de un local stdin
, que por supuesto se cierra tan pronto como ssh session
finaliza. Y esto provocará la terminación remota de los precios cada vez que intente acceder a su stdin
.
Respuesta4
Esto suena más como un problema con lo que hace el python
guión o él mismo. python
Todo lo que nohup
realmente hace (barra que simplifica las redirecciones) es simplemente configurar el controlador de la HUP
señal para SIG_IGN
(ignorar) antes de ejecutar el programa. No hay nada que impida que el programa vuelva a configurarlo SIG_DFL
o instale su propio controlador una vez que comienza a ejecutarse.
Una cosa que quizás quieras probar es encerrar tu comando entre paréntesis para obtener un efecto de doble bifurcación y tu python
script ya no sea hijo del proceso de shell. P.ej:
( nohup python3 -u <script> & )
Otra cosa que también puede valer la pena probar (si estás usando bash
y no otro shell) es usar el disown
shell incorporado en lugar de nohup
. Si todo funciona según lo documentado, esto no debería hacer ninguna diferencia, pero en un shell interactivo esto evitaría que la HUP
señal se propague a su python
secuencia de comandos. Puede agregar el rechazo en la siguiente línea o en la misma que se muestra a continuación (tenga en cuenta que agregar un ;
después de a &
es un error en bash
):
python3 -u <script> </dev/null &>/dev/null & disown
Si lo anterior o alguna combinación de ellos no funciona, entonces seguramente el único lugar para abordar el problema es en el python
propio script.