¿Cómo mantener Bash ejecutándose después de la ejecución del comando?

29

Me gustaría ejecutar algo como esto:

bash -c "some_program with its arguments"

pero para que un bash interactivo siga ejecutándose después de some_programfinalizar.

Estoy seguro de -cque no es una buena manera como man bashseys:

Un shell interactivo es uno que se inicia sin argumentos sin opciones y sin la opción -c

Entonces, ¿cómo hacer esto?

El objetivo principal se describe aquí.

NOTA

  • Necesito terminar some_programde vez en cuando
  • No quiero ponerlo en segundo plano
  • Quiero quedarme en el bashentonces para hacer otra cosa
  • Quiero poder ejecutar el programa nuevamente
pawel7318
fuente
1
Si el objetivo es tan complejo, también debe explicarlo aquí. pero eso es solo un consejo
Kiwy
1
Traté de describir lo más breve posible aquí y con mucha precisión. Pero no esperaba que la mayoría de la gente no se concentrara en los detalles e intentara proponer algo más. Pondré algunas notas para que quede claro.
pawel7318
¿Para qué son los escapes de terminal en esa otra pregunta? El objetivo que describe es factible, pero requerirá el manejo de E / S. Su subshell promedio no va a manejar fácilmente las E / S escapadas a través de un archivo normal. Deberías investigarlopty.
mikeserv
La única razón por la que funciona en mi respuesta a continuación, por cierto, es porque robo el terminal. Sin embargo, con el tiempo, es probable que el proceso principal lo recupere, y luego está viendo buffers de 4kb.
mikeserv
¿Por qué no quieres poner un programa en segundo plano? Comience en segundo plano, haga un poco de golpe, póngalo en primer plano confg
Bernhard

Respuestas:

8
( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
  exec  3>&- <&4
  SCRIPT
)

Sin embargo, esto se hace mejor desde una secuencia de comandos con exec $0.O si uno de esos descriptores de archivo dirige a un dispositivo terminal que no se está utilizando actualmente, será de ayuda; debe recordar, otros procesos también quieren verificar ese terminal.

Y, por cierto, si su objetivo es, como supongo que es, preservar el entorno del script después de ejecutarlo, probablemente sea mucho mejor servido con:

. ./script

Los shell .doty bash's sourceno son uno y el mismo: el shell .dotestá POSIX especificado como un shell especial incorporado y, por lo tanto, está lo más cerca posible de estar garantizado, aunque esto de ninguna manera es una garantía de que estará allí ...

Aunque lo anterior debería hacer lo que espera con poco problema. Por ejemplo, puedes:

 ( exec sh -i 3<<SCRIPT 4<&0 <&3                                        
    echo "do this thing"
    echo "do that thing"
    $(cat /path/to/script)
    exec  3>&- <&4
    SCRIPT
 )

El shell ejecutará su script y lo regresará a la solicitud interactiva, siempre que evite exitel shell de su script, es decir, o en segundo plano su proceso, que vinculará su E / S a/dev/null.

MANIFESTACIÓN:

% printf 'echo "%s"\n' "These lines will print out as echo" \
    "statements run from my interactive shell." \
    "This will occur before I'm given the prompt." >|/tmp/script
% ( exec sh -i 3<<SCRIPT 4<&0 <&3
    echo "do this thing"
    echo "do that thing"
    $(cat /tmp/script)
    exec  3>&- <&4
SCRIPT
)
sh-4.3$ echo "do this thing"
    do this thing
sh-4.3$ echo "do that thing"
    do that thing
sh-4.3$ echo "These lines will print out as echo"
    These lines will print out as echo
sh-4.3$ echo "statements run from my interactive shell."
    statements run from my interactive shell.
sh-4.3$ echo "This will occur before I'm given the prompt."
    This will occur before I'm given the prompt.
sh-4.3$ exec  3>&- <&4
sh-4.3$

MUCHOS JOBS

Es mi opinión que debería familiarizarse un poco más con las opciones de administración de tareas integradas del shell. @Kiwy y @jillagre ya han mencionado esto en sus respuestas, pero podría justificar más detalles. Y ya he mencionado un proyectil especial especificado por POSIX incorporado, pero set, jobs, fg,y bgson un poco más, e, como otra respuesta demuestra trapykill dos más todavía.

Si aún no recibe notificaciones instantáneas sobre el estado de la ejecución simultánea de procesos en segundo plano, es porque sus opciones de shell actuales están configuradas en el valor predeterminado especificado por POSIX -m, pero en su lugar puede obtenerlas de forma asincrónica set -b:

% man set
    b This option shall be supported if the implementation supports the
         User  Portability  Utilities  option. It shall cause the shell to
         notify the user asynchronously of background job completions. The
         following message is written to standard error:
             "[%d]%c %s%s\n", <job-number>, <current>, <status>, <job-name>

         where the fields shall be as follows:

         <current> The  character  '+' identifies the job that would be
                     used as a default for the fg or  bg  utilities;  this
                     job  can  also  be specified using the job_id "%+" or
                     "%%".  The character  '−'  identifies  the  job  that
                     would  become  the default if the current default job
                     were to exit; this job can also  be  specified  using
                     the  job_id  "%−".   For  other jobs, this field is a
                     <space>.  At most one job can be identified with  '+'
                     and  at  most one job can be identified with '−'.  If
                     there is any suspended  job,  then  the  current  job
                     shall  be  a suspended job. If there are at least two
                     suspended jobs, then the previous job also shall be a
   m  This option shall be supported if the implementation supports the
         User Portability Utilities option. All jobs shall be run in their
         own  process groups. Immediately before the shell issues a prompt
         after completion of the background job, a message  reporting  the
         exit  status  of  the background job shall be written to standard
         error. If a foreground job stops, the shell shall write a message
         to  standard  error to that effect, formatted as described by the
         jobs utility. In addition, if a job  changes  status  other  than
         exiting  (for  example,  if  it  stops  for input or output or is
         stopped by a SIGSTOP signal), the shell  shall  write  a  similar
         message immediately prior to writing the next prompt. This option
         is enabled by default for interactive shells.

Una característica muy fundamental de los sistemas basados ​​en Unix es su método de proceso de manejo signals. Una vez leí un artículo esclarecedor sobre el tema que compara este proceso con la descripción de Douglas Adams del planeta NowWhat:

"En The Hitchhiker's Guide to the Galaxy, Douglas Adams menciona un planeta extremadamente aburrido, habitado por un grupo de humanos deprimidos y una cierta raza de animales con dientes afilados que se comunican con los humanos mordiéndolos muy fuerte en los muslos. Esto es sorprendente similar a UNIX, en el cual el núcleo se comunica con los procesos enviándoles señales paralizantes o mortales. Los procesos pueden interceptar algunas de las señales e intentar adaptarse a la situación, pero la mayoría de ellas no lo hacen ".

Esto se refiere a kill signals.

% kill -l 
> HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH POLL PWR SYS

Al menos para mí, la cita anterior respondió muchas preguntas. Por ejemplo, siempre consideré muy extraño y nada intuitivo que si quería monitorear un ddproceso tenía quekill hacerlo. Después de leer eso tenía sentido.

Diría que la mayoría de ellos no intentan adaptarse por una buena razón: puede ser una molestia mucho mayor de lo que sería una bendición tener un montón de procesos enviando spam a su terminal con cualquier información que sus desarrolladores pensaran que podría haber sido importante para usted .

Dependiendo de la configuración de su terminal (con la que puede verificar stty -a) , CTRL+Zes probable que esté configurado para reenviar SIGTSTPa al líder actual del grupo de procesos en primer plano, que probablemente sea su shell, y que también debe configurarse de manera predeterminada para trapesa señal y suspender su último comando. Nuevamente, como muestran las respuestas de @jillagre y @Kiwy juntas, no hay impedimento para adaptar esta funcionalidad a su propósito como prefiera.

SCREEN JOBS

Por lo tanto, para aprovechar estas características, se espera que primero las comprenda y personalice su manejo según sus propias necesidades. Por ejemplo, acabo de encontrar este screenrc en Github que incluye combinacionesscreen de teclas para SIGTSTP:

# hitting 'C-z C-z' will run Ctrl+Z (SIGTSTP, suspend as usual)
bind ^Z stuff ^Z

# hitting 'C-z z' will suspend the screen client
bind z suspend

Eso facilitaría la suspensión de un proceso que se ejecuta como un screenproceso secundario oscreen proceso secundario en sí mismo como lo desee.

E inmediatamente después:

% fg  

O:

% bg

Pondría en primer plano o en segundo plano el proceso como prefiera. El jobsincorporado puede proporcionarle una lista de estos en cualquier momento. Agregar el -loperando incluirá detalles pid.

mikeserv
fuente
Se ve muy interesante. Podré probar todo más tarde hoy.
pawel7318
23

Aquí hay una solución más corta que logra lo que desea, pero podría no tener sentido a menos que comprenda el problema y cómo funciona bash:

bash -i <<< 'some_program with its arguments; exec </dev/tty'

Esto lanzará un shell bash, comenzará some_programy, después de las some_programsalidas, se lo colocará en un shell bash.

Básicamente, lo que estamos haciendo es alimentar a bash con una cadena en su STDIN. Esa cuerda es some_program with its arguments; exec </dev/tty. Esto le dice a bash que se inicie some_programprimero y luego se ejecute exec </dev/ttydespués. Entonces, en lugar de continuar leyendo comandos de la cadena que lo pasamos, bash comenzará a leer /dev/tty.

Esto -ise debe a que cuando se inicia bash, verifica si STDIN es un tty, y cuando se inicia no lo es. Pero más tarde lo será, así que lo forzamos al modo interactivo.


Otra solución

Otra idea que pensé que sería muy portátil sería agregar lo siguiente al final de su ~/.bashrcarchivo.

if [[ -n "START_COMMAND" ]]; then
  start_command="$START_COMMAND"
  unset START_COMMAND
  eval "$start_command"
fi

Luego, cuando desee iniciar un shell con un comando primero, simplemente haga:

START_COMMAND='some_program with its arguments' bash

Explicación:

La mayor parte de esto debería ser obvio, pero la resonancia para el cambio de nombre de la variable es para que podamos localizar la variable. Como $START_COMMANDes una variable exportada, será heredada por cualquier hijo del shell, y si otro shell bash es uno de esos hijos, ejecutará el comando nuevamente. Entonces asignamos el valor a una nueva variable no exportada ( $start_command) y eliminamos la anterior.

Patricio
fuente
Volar si hay más de uno, ¿qué? Un comando? No, todavía debería funcionar. Sin embargo, tienes razón sobre la portabilidad. Hay 2 factores allí, el <<<no es POSIX, pero podría en su echo "string here" | bash -ilugar. Luego está lo /dev/ttyque es una cosa de Linux. Pero podría duplicar el FD antes de iniciar bash, y luego volver a abrir STDIN, que se parece a lo que está haciendo, pero opté por mantener las cosas simples.
Patrick
ok ok solo no pelees. Esto solo funciona para mí y hace todo lo que necesito. No me importa mucho POSIX y la portabilidad, lo necesito en mi caja y solo allí. Comprobaré también la respuesta de mikeserv, pero no puedo hacer esto ahora.
pawel7318
1
No pelear :-). Respeto la respuesta de mikserv. Él fue por compatibilidad, yo fui por simplicidad. Ambos son completamente válidos. A veces buscaré compatibilidad si no es demasiado complejo. En este caso no pensé que valiera la pena.
Patrick
2
@Patrick, /dev/ttyno es una cosa de Linux, pero definitivamente POSIX.
jlliagre
2
Hola, ¿ qué sabes ?
Patrick
8

Esto debería funcionar:

bash -c "some_program with its arguments;bash"

Editar:

Aquí hay un nuevo intento después de su actualización:

bash -c "
trap 'select wtd in bash restart exit; do [ \$wtd = restart ] && break || \$wtd ; done' 2
while true; do
    some_program with its arguments
done
"
  • Necesito terminar algún_programa de vez en cuando

Use ControlC, se le presentará este pequeño menú:

1) bash
2) restart
3) exit
  • No quiero ponerlo en segundo plano

Ese es el caso.

  • Quiero quedarme en la fiesta y luego hacer otra cosa

Seleccione la opción "bash"

  • Quiero poder ejecutar el programa nuevamente

Seleccione la opción "reiniciar"

jlliagre
fuente
no está mal, no está mal ... pero también sería bueno poder volver a ese último comando. ¿Alguna idea para eso? Consulte mi otra pregunta aquí para ver para qué la necesito. Entonces, tal vez sugiera otro enfoque
pawel7318
Esto ejecuta una subshell. Creo que el autor de la pregunta está tratando de preservar el entorno del script.
mikeserv
gracias jlliagre - buen intento pero no muy útil para mí. Usualmente presiono ctrl + C y espero que haga lo que se supone que debe hacer. El menú adicional es demasiado.
pawel7318
@ pawel7318 el comportamiento de CTRL+Ces solo un efecto secundario de la configuración predeterminada de su terminal para interpretarlo como a SIGINT. Puede modificar eso como lo hará con stty.
mikeserv
@ pawel7318 para aclarar más, SIGINTestá trappedarriba con 2.
mikeserv
3

Puede hacerlo pasando su script como un archivo de inicialización:

bash --init-file foo.script

O puede pasarlo en la línea de comando:

bash --init-file <(echo "ls -al")

Tenga en cuenta que --init-file estaba destinado a leer archivos de inicialización de todo el sistema, /etc/bash.bashrcpor lo que es posible que desee ' source' estos en su secuencia de comandos.

jeroent
fuente
0

Realmente no veo el punto de hacer esto, ya que ya vuelves a un shell después de ejecutar este programa. Sin embargo, podrías hacer esto:

bash -c "some_program with its arguments; bash"

Esto lanzará una fiesta interactiva después de que se ejecute el programa.

mtak
fuente
Eso lanza una subshell.
mikeserv
0

Puede poner el comando en segundo plano para mantener abierto su bash actual:

some_program with its arguments &

Para volver a la ejecución, puede usar el fgcomando y ^+zvolver a ponerlo en segundo plano.

Kiwy
fuente
no esta vez. Asumes que quiero ejecutar este bash ... desde bash, que no es el caso.
pawel7318
@ pawel7318 si explicas un poco más, ¿podríamos darte una mejor respuesta, tal vez también?
Kiwy
Por favor revise mi otra pregunta aquí .
pawel7318
@ pawel7318 si su pregunta está relacionada, agregue el enlace a la otra pregunta en su propia pregunta.
Kiwy
1
@ pawel7318 basho no, estás usando un shell muy extraño para mí si no puede manejar el fondo del proceso. Y estoy de acuerdo con Kiwi: esa información le serviría mejor si estuviera en su pregunta.
mikeserv
0

Es posible que desee utilizar la pantalla para ejecutar el comando. Luego puede volver a adjuntar a la sesión una vez que se complete el comando.

Alternativamente, simplemente ejecute el comando en segundo plano some_program with its arguments&. Esto le permitirá volver a ejecutar el comando y obtener el estado del comando una vez que se haya completado.

BillThor
fuente
Lo estoy ejecutando exactamente, screenpero ponerlo en segundo plano no es útil para mí; a veces necesito terminar el programa, hacer otra cosa y ejecutarlo nuevamente. Y el objetivo principal es hacer esto rápido.
pawel7318
@ pawel7318 Puede matar el programa en segundo plano con el comando kill %o kill %1.
BillThor