Das kaufmännische Und „variabilisieren“ (Hintergrund eines Prozesses)

9

Ich möchte wissen, ob es eine Möglichkeit gibt, das kaufmännische Und in eine Variable einzufügen und dennoch einen Prozess in den Hintergrund zu senden.

Das funktioniert:

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

Aber wäre es nicht cool, diese fünf Zeilen mit nur einer zu erreichen? Wie so:

BCKGRND='&'
sleep 5 ${BCKGRND}

Das geht aber nicht Wenn BCKGRND nicht gesetzt ist, funktioniert es - aber wenn es gesetzt ist, wird es als wörtliches '&' interpretiert und es treten Fehler auf.

BrowncoatOkie
quelle
Nach Verwendung des nachgestellten kaufmännischen Und- echo $!
Zeichens wird

Antworten:

9

Es ist nicht möglich, eine Variable als Hintergrund für den Aufruf zu verwenden, da die Variablenerweiterung erfolgt, nachdem die Befehlszeile für Steuerungsoperatoren (wie &&und &) analysiert wurde .

Eine weitere Option wäre, die Aufrufe in eine Funktion zu packen:

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

... und stellen Sie dann die Variable nach Bedarf ein:

$ 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
quelle
Was, nein ed? +1 sowieso, das ist die sauberste Lösung.
Stephen Kitt
LOL @StephenKitt; Jetzt drehen sich die Gänge
Jeff Schaller
Ich mochte die eval-Antwort wegen ihrer Einfachheit, aber mein tatsächlicher realer Befehl, den ich als Hintergrund verwenden wollte, war viel zu kompliziert, da zu viele Variablen darin enthalten waren, um mit eval vertraut zu sein. @ jeff-schaller gab die antwort, die mich in die richtung zeigte, in die ich ging. Anstelle einer Funktion habe ich jedoch den gesamten Befehl in eine Variable eingefügt und dann seinen if-Anweisungsstil verwendet, um den Befehl mit oder ohne & auszuführen.
BrowncoatOkie
11

Sie können Dinge umdrehen und den „Vordergrund“ variieren:

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Stellen Sie FOREGROUNDauf trueoder leeren Sie den Prozess im Hintergrund laufen zu lassen. (Die Einstellung FOREGROUND, trueim Hintergrund ausgeführt zu werden, ist zugegebenermaßen verwirrend! Geeignete Variablennamen werden dem Leser als Übung überlassen.)

Stephen Kitt
quelle
4
Das ist schön, aber es funktioniert nicht in einer Shell ohne Jobsteuerung (dh in jedem Skript, sofern set -mes nicht verwendet wurde).
Mosvy
9

Sie müssten wahrscheinlich verwenden eval:

eval "sleep 5" "$BCKGRND"

evalbewirkt, dass die Shell die angegebenen Argumente neu bewertet. Ein Literal &würde daher als &am Ende eines Befehls und nicht als Argument für den Befehl interpretiert , wodurch der Befehl in den Hintergrund gestellt wird.

Kusalananda
quelle
2
Eine Antwort, die enthält, evalsollte eine Warnung enthalten, dass dies mit Vorsicht behandelt werden sollte. Siehe zB diese Antwort .
Ralf
Ich verstehe nicht, was das Problem ist, wenn "$BCKGRND"man ein leeres Argument bewertet.
Mosvy
2
@Ralf in diesem Fall absolut irrelevant . Eval hat nichts Besonderes - Sie können Befehle beispielsweise über arithmetische Erweiterungen ausführen. Vielleicht sollte es eine solche Warnung gegen die Verwendung von Bash (oder einer ähnlichen Shell) geben
;-)
1
@Kusalananda evalwird seine Argumente mit Leerzeichen verbinden, bevor die eigentliche Bewertung durchgeführt wird. Probieren Sie es einfach aus : eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""ist völlig ähnlich eval foo, egal was IFSoder was anderes ist.
Mosvy
1
Der zu bewertende Befehl sollte in doppelten Anführungszeichen stehen, wenn er Sonderzeichen enthält, z eval 'sleep $TIMEOUT' "$BACKGROUND". Andernfalls können Sie doppelte Erweiterungen erhalten, wenn die Variable zu einer anderen Variablen erweitert wird oder Sonderzeichen enthält. Auch verschachtelte Zitate können schwierig werden.
Barmar