Ejecutar comandos de bash en segundo plano sin imprimir los ID de proceso y trabajo

83

Ejecutar un proceso en segundo plano en bash es bastante fácil.

$ echo "Hello I'm a background task" &
[1] 2076
Hello I'm a background task
[1]+  Done                    echo "Hello I'm a background task"

Sin embargo, el resultado es detallado. En la primera línea se imprime la identificación del trabajo y la identificación del proceso de la tarea en segundo plano, luego tenemos la salida del comando, finalmente tenemos la identificación del trabajo, su estado y el comando que activó el trabajo.

¿Hay alguna forma de suprimir el resultado de ejecutar una tarea en segundo plano de modo que el resultado se vea exactamente como lo haría sin el signo comercial al final? Es decir:

$ echo "Hello I'm a background task" &
Hello I'm a background task

La razón por la que pregunto es que quiero ejecutar un proceso en segundo plano como parte de un comando de finalización de tabulación, por lo que la salida de ese comando debe ser ininterrumpida para que tenga sentido.

Alex Spurling
fuente
Supongo que debería agregar 2> / dev / null a todo lo que haga dentro de los scripts de finalización de bash
consulte el

Respuestas:

98

No relacionado con la finalización, pero puede suprimir esa salida poniendo la llamada en una subcapa:

(echo "Hello I'm a background task" &)
Dimitre Radoulov
fuente
3
Gracias, eso definitivamente funciona. Desafortunadamente, cuando se ejecuta un script como este desde una función de finalización de bash, parece que bash espera hasta que se completa el subproceso antes de dar los resultados de finalización. Esto es lamentable, ya que significa que no parece posible hacer que los trabajadores en segundo plano se activen al completar bash.
Alex Spurling
13
No puede usar esto si desea waitque finalice el trabajo en segundo plano.
arekolek
13

Partiendo de la respuesta de @ shellter, esto funcionó para mí:

tyler@Tyler-Linux:~$ { echo "Hello I'm a background task" & disown; } 2>/dev/null; sleep .1;
Hello I'm a background task
tyler@Tyler-Linux:~$

No conozco el razonamiento detrás de esto, pero recordé de una publicación anterior que el rechazo evita que bash genere los ID del proceso.

Tyzoid
fuente
@Tyzoid. Probé su solución en un shell bash (v 4.2.37) y obtuve resultados interesantes. 1. Obtuve los mismos resultados solo agregando ;al final. (sin dormir) . 2. PERO noté que cuando agregué echo STDERR 2>&1en la línea "siguiente", [1]+ Done ...¡apareció la cosa! . 3. Mi solución (como ya he señalado) funciona kshy echo STDERR 2>&1solo produce la salida STDERR. Bueno, vive y aprende para los dos. Buena suerte a todos.
shellter
12

En algunas versiones más nuevas de bash y en ksh93, puede rodearlo con un sub-shell o grupo de procesos (es decir { ... }).

/home/shellter $ { echo "Hello I'm a background task" & } 2>/dev/null
Hello I'm a background task
/home/shellter $
shellter
fuente
5
Cuando uso llaves, la última línea de salida todavía se muestra (no estoy seguro de por qué no la ve).
Alex Spurling
Copié esto exactamente pero no me funciona, todavía muestra la salida como dice @AlexSpurling. Tal vez estoy haciendo algo mal desde que otros lo votaron a favor, pero me temo que tengo que dar -1.
Mark
1
@Mark: Acabo de hacer algunas pruebas rápidas con esto y veo que el problema es que continúo ksh93+usándolo como mi shell principal. Este código funciona según lo prometido en ksh. pero como el OP está etiquetado con bash, entiendo su dv. Vea los resultados de la prueba bash en la respuesta de @ Tyzoid. ¡Buena suerte a todos!
shellter
Como dejaste claro que esto no funciona en bash, eliminaré el -1.
Mark
1
Para mí, esto funciona muy bien en una función Bash, también se puede esperar el proceso con wait $!lo que no parece ser el caso de la respuesta aceptada.
Jonatan Öström
6

Sobre la base de la respuesta anterior, si necesita permitir que stderr llegue desde el comando:

f() { echo "Hello I'm a background task" >&2; }
{ f 2>&3 &} 3>&2 2>/dev/null
pajamian
fuente
Hermoso uso de redirecciones. Aún así, necesitará un disownen algunas circunstancias .
Tom Hale
6

Basado en esta respuesta , se me ocurrió la más concisa y correcta:

silent_background() {
    { 2>&3 "$@"& } 3>&2 2>/dev/null
    disown &>/dev/null  # Prevent whine if job has already completed
}
silent_background date
Tom Hale
fuente
¡Perfecto! y reutilizable para otras cosas
user230910
5

Tratar:

user@host:~$ read < <( echo "Hello I'm a background task" & echo $! )
user@host:~$ echo $REPLY
28677

Y ha ocultado tanto la salida como el PID . Tenga en cuenta que aún puede recuperar el PID de $ REPLY

mark_infinite
fuente
1
¡Esto es perfecto! Gracias
Adham Atta
4

Perdón por la respuesta a una publicación anterior, pero creo que esto es útil para otros y es la primera respuesta en Google.

Tenía un problema con este método (subcapas) y usaba 'esperar'. Sin embargo, como lo estaba ejecutando dentro de una función, pude hacer esto:

function a {
    echo "I'm background task $1"
    sleep 5
}

function b {
    for i in {1..10}; do
        a $i &
    done
    wait
} 2>/dev/null

Y cuando lo ejecuto:

$ b
I'm background task 1
I'm background task 3
I'm background task 2
I'm background task 4
I'm background task 6
I'm background task 7
I'm background task 5
I'm background task 9
I'm background task 8
I'm background task 10

Y hay un retraso de 5 segundos antes de que recupere mi mensaje.

El Tomahawk
fuente
1

La solución de subshell funciona, pero también quería poder esperar en los trabajos en segundo plano (y no tener el mensaje "Listo" al final). $!de un subshell no es "waitable" en el shell interactivo actual. La única solución que funcionó para mí fue usar mi propia función de espera, que es muy simple:

myWait() {
  while true; do
    sleep 1; STOP=1
    for p in $*; do
      ps -p $p >/dev/null && STOP=0 && break
    done
    ((STOP==1)) && return 0
  done
}

i=0
((i++)); p[$i]=$(do_whatever1 & echo $!)
((i++)); p[$i]=$(do_whatever2 & echo $!)
..
myWait ${p[*]}

Suficientemente fácil.

ExpertNoob1
fuente