"Variabilizar" el ampersand (antecedentes un proceso)

9

Quiero saber si hay una manera de poner el ampersand en una variable y aún usarlo para enviar un proceso a un segundo plano.

Esto funciona:

BCKGRND=yes
if [ "$BCKGRND" = "yes" ]; then
    sleep 5 &
else
    sleep 5
fi

¿Pero no sería genial lograr esas cinco líneas con una sola? Al igual que:

BCKGRND='&'
sleep 5 ${BCKGRND}

Pero eso no funciona. Si BCKGRND no está configurado, funciona, pero cuando está configurado, se interpreta como un '&' literal y elimina los errores.

BrowncoatOkie
fuente
después de usar el signo &, echo $!devuelve el PID
noobninja

Respuestas:

9

No es posible usar una variable para poner en segundo plano la llamada porque la expansión de la variable ocurre después de que la línea de comandos se analiza para los operadores de control (como &&y &).

Otra opción sería envolver las llamadas en una función:

mayberunbg() {
  if [ "$BCKGRND" = "yes" ]; then
    "$@" &
  else
    "$@"
  fi
}

... y luego establezca la variable según sea necesario:

$ BCKGRND=yes mayberunbg sleep 3
[1] 14137
$
[1]+  Done                    "$@"
# or
$ BCKGRND=yes
$ mayberunbg sleep 3
[1] 14203
$
[1]+  Done                    "$@"
$ BCKGRND=no mayberunbg sleep 3
# 3 seconds later
$
Jeff Schaller
fuente
Qué, no hay ed? +1 de todos modos, esta es la solución más limpia.
Stephen Kitt el
LOL @StephenKitt; ahora los engranajes están girando
Jeff Schaller
Me gustó la respuesta eval por su simplicidad, pero mi comando real del mundo real que quería poner en segundo plano era demasiado complicado con demasiadas variables como para ser cómodo usando eval. @ jeff-schaller dio la respuesta que me señaló en la dirección en que fui. Sin embargo, en lugar de una función, puse todo el comando en una variable y luego usé su estilo de instrucción if para ejecutar el comando con o sin el &.
BrowncoatOkie
11

Puede voltear cosas y variabilizar el "primer plano":

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Establecer FOREGROUNDa trueo vaciar para ejecutar el proceso en segundo plano. (¡La configuración FOREGROUNDpara trueejecutarse en segundo plano es ciertamente confusa! Los nombres de variables apropiados se dejan como ejercicio para el lector).

Stephen Kitt
fuente
44
eso es bueno, pero no funcionará en un shell sin control de trabajo (es decir, cualquier script a menos que set -mse haya utilizado).
mosvy
9

Probablemente deberías usar eval:

eval "sleep 5" "$BCKGRND"

evalhace que el shell vuelva a evaluar los argumentos dados. &Por lo tanto, un literal se interpretaría como &al final de un comando y no como un argumento para el comando, poniendo el comando en segundo plano.

Kusalananda
fuente
2
Una respuesta que contenga evaldebe contener una advertencia, que debe manejarse con cuidado. Ver, por ejemplo, esta respuesta .
Ralf
No entiendo cuál es el problema al "$BCKGRND"evaluar un argumento vacío.
mosvy
2
@Ralf absolutamente irrelevante en este caso . Eval no tiene nada de especial: por ejemplo, puede ejecutar comandos mediante expansiones aritméticas. Tal vez debería haber tal advertencia contra el uso de bash (o cualquier shell similar) en absoluto ;-)
mosvy
1
@Kusalananda evalunirá sus argumentos con espacios antes de realizar la evaluación real. Haga la prueba: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""es completamente similar a eval foo, no importa qué IFSu otra cosa sea.
mosvy
1
El comando que se evalúa debe estar entre comillas dobles si contiene caracteres especiales, por ejemplo eval 'sleep $TIMEOUT' "$BACKGROUND". De lo contrario, podría obtener expansiones dobles si la variable se expande a otra variable o contiene caracteres especiales. Además, las citas anidadas pueden ser complicadas.
Barmar