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:
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:
interact()
regresará inmediatamente ya que no hay nada que hacer y el fg
no hará nada.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.