«Variabiliser» l'esperluette (en arrière-plan un processus)

9

Je veux savoir s'il existe un moyen de mettre l'esperluette dans une variable tout en l'utilisant pour envoyer un processus en arrière-plan.

Cela marche:

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

Mais ne serait-ce pas cool d'accomplir ces cinq lignes avec une seule? Ainsi:

BCKGRND='&'
sleep 5 ${BCKGRND}

Mais ça ne marche pas. Si BCKGRND n'est pas défini, cela fonctionne - mais lorsqu'il est défini, il est interprété comme un «&» littéral et les erreurs sont supprimées.

BrowncoatOkie
la source
après avoir utilisé l'esperluette de fin, echo $!retourne le PID
noobninja

Réponses:

9

Il n'est pas possible d'utiliser une variable pour mettre en arrière-plan l'appel, car l'expansion des variables se produit après que la ligne de commande a été analysée pour les opérateurs de contrôle (tels que &&et &).

Une autre option serait d'encapsuler les appels dans une fonction:

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

... puis définissez la variable selon vos besoins:

$ 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
la source
Quoi, non ed? +1 de toute façon, c'est la solution la plus propre.
Stephen Kitt
LOL @StephenKitt; maintenant les engrenages tournent
Jeff Schaller
J'ai aimé la réponse eval pour sa simplicité, mais ma commande réelle du monde réel que je voulais mettre en arrière-plan était beaucoup trop compliquée avec trop de variables pour être à l'aise avec eval. @ jeff-schaller a donné la réponse qui m'a indiqué la direction dans laquelle j'allais. Au lieu d'une fonction, cependant, j'ai mis la commande entière dans une variable, puis j'ai utilisé son style d'instruction if pour exécuter la commande avec ou sans le &.
BrowncoatOkie
11

Vous pouvez inverser les choses et variabiliser le «premier plan»:

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Définissez FOREGROUNDsur trueou vide pour exécuter le processus en arrière-plan. (Le réglage FOREGROUNDde trues'exécuter en arrière-plan est certes déroutant! Les noms de variables appropriés sont laissés à la discrétion du lecteur.)

Stephen Kitt
la source
4
c'est bien mais cela ne fonctionnera pas dans un shell sans contrôle de travail (c'est-à-dire n'importe quel script sauf s'il a set -mété utilisé).
mosvy
9

Vous devrez probablement utiliser eval:

eval "sleep 5" "$BCKGRND"

evaloblige le shell à réévaluer les arguments donnés. Un littéral &serait donc interprété comme &à la fin d'une commande et non comme un argument de la commande, mettant la commande en arrière-plan.

Kusalananda
la source
2
Une réponse contenant evaldoit contenir un avertissement, que cela doit être manipulé avec soin. Voir par exemple cette réponse .
Ralf
Je ne comprends pas quel est le problème avec l' "$BCKGRND"évaluation d'un argument vide.
mosvy
2
@Ralf absolument hors de propos dans ce cas . Eval n'a rien de spécial - vous pouvez exécuter des commandes via des extensions arithmétiques, par exemple. Peut-être qu'il devrait y avoir un tel avertissement contre l'utilisation de bash (ou tout autre shell similaire) ;-)
mosvy
1
@Kusalananda evaljoindra ses arguments avec des espaces avant de faire l'évaluation réelle. Il suffit de l' essayer: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""est complètement similaire à eval foo, peu importe quoi IFSou autre chose.
mosvy
1
La commande en cours d'évaluation doit être entre guillemets si elle contient des caractères spéciaux, par exemple eval 'sleep $TIMEOUT' "$BACKGROUND". Sinon, vous pourriez obtenir des extensions doubles si la variable se développe en une autre variable ou contient des caractères spéciaux. En outre, les citations imbriquées peuvent devenir délicates.
Barmar