unix shell – Ejecutar comando en segundo plano con acceso a la terminal en primer plano

Pregunta:

Estoy tratando de crear una función que pueda ejecutar un comando arbitrario, interactuar con el proceso secundario (se omiten los detalles) y luego esperar a que salga. Si tiene éxito, escribir run <command> parecerá comportarse como un <command> simple.

Si no estuviera interactuando con el proceso hijo, simplemente escribiría:

run() {
    "$@"
}

Pero debido a que necesito interactuar con él mientras se ejecuta, tengo esta configuración más complicada con coproc y wait .

run() {
    exec {in}<&0 {out}>&1 {err}>&2
    { coproc "$@" 0<&$in 1>&$out 2>&$err; } 2>/dev/null
    exec {in}<&- {out}>&- {err}>&-

    # while child running:
    #     status/signal/exchange data with child process

    wait
}

(Esto es una simplificación. Si bien el coproc y todas las redirecciones no están haciendo nada útil aquí que "$@" & no pudieran hacer, los necesito todos en mi programa real).

El comando "$@" puede ser cualquier cosa. La función que tengo funciona con run ls y run make y similares, pero falla cuando run vim . Falla, supongo, porque Vim detecta que es un proceso en segundo plano y no tiene acceso a la terminal, por lo que en lugar de abrir una ventana de edición, se suspende. Quiero arreglarlo para que Vim se comporte normalmente.

¿Cómo puedo hacer que coproc "$@" ejecute en "primer plano" y el shell padre se convierta en "fondo"? La parte "interactuar con el niño" no lee ni escribe en la terminal, por lo que no necesito que se ejecute en primer plano. Me complace ceder el control del tty al coproceso.

Es importante para lo que estoy haciendo que run() esté en el proceso padre y "$@" en su hijo. No puedo intercambiar esos roles. Pero puedo intercambiar el primer plano y el fondo. (Simplemente no sé cómo hacerlo).

Tenga en cuenta que no estoy buscando una solución específica de Vim. Y preferiría evitar pseudo-ttys. Mi solución ideal funcionaría igualmente bien cuando stdin y stdout están conectados a un tty, a tuberías o se redirigen desde archivos:

run echo foo                               # should print "foo"
echo foo | run sed 's/foo/bar/' | cat      # should print "bar"
run vim                                    # should open vim normally

¿Por qué utilizar coprocesos?

Podría haber escrito la pregunta sin coproc, con solo

run() { "$@" & wait; }

Obtengo el mismo comportamiento con solo & . Pero en mi caso de uso, estoy usando la configuración de FIFO coproc y pensé que era mejor no simplificar demasiado la pregunta en caso de que haya una diferencia entre cmd & y coproc cmd .

¿Por qué evitar los ptys?

run() podría usarse en un contexto automatizado. Si se usa en una canalización o con redirecciones, entonces no habría ninguna terminal para emular; montar un pty sería un error.

¿Por qué no usar esperar?

No estoy tratando de automatizar vim, enviarle ninguna entrada ni nada por el estilo.

Respuesta:

He agregado código para que:

  • funciona para tus tres ejemplos y
  • la interacción viene antes de la espera.
  • interact() {
        pid=$1
        ! ps -p $pid && return
        ls -ld /proc/$pid/fd/*
        sleep 5; kill -1 $pid   # TEST SIGNAL TO PARENT
    }
    
    run() {
        exec {in}<&0 {out}>&1 {err}>&2
        { coproc "$@" 0<&$in 1>&$out 2>&$err; } 2>/dev/null
        exec {in}<&- {out}>&- {err}>&-
        { interact $! <&- >/tmp/whatever.log 2>&1& } 2>/dev/null
        fg %1 >/dev/null 2>&1
        wait 2>/dev/null
    }
    

    El fg %1 se ejecutará para todos los comandos (cambie %1 según sea necesario para trabajos simultáneos) y, en circunstancias normales, sucederá una de dos cosas:

  • Si el comando sale inmediatamente, interact() regresará inmediatamente ya que no hay nada que hacer y el fg no hará nada.
  • Si el comando no sale inmediatamente, interact() puede interactuar (por ejemplo, enviar un HUP al coproceso después de 5 segundos) y el fg pondrá el coproceso en primer plano usando el mismo stdin / out / err con el que se ejecutó originalmente ( puede verificar esto con ls -l /proc/<pid>/df ).
  • Las redirecciones a / dev / null en los últimos tres comandos son cosméticas. Permiten que run <command> aparezca exactamente igual que si hubiera ejecutado un command por sí solo.

    Leave a Comment

    Your email address will not be published. Required fields are marked *

    web tasarım