„Variabilize” ampersand (tło proces)

9

Chcę wiedzieć, czy istnieje sposób na umieszczenie ampersand w zmiennej i nadal używanie go do wysyłania procesu do tła.

To działa:

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

Ale czy nie byłoby fajnie zrealizować te pięć linii za pomocą tylko jednej? Tak jak:

BCKGRND='&'
sleep 5 ${BCKGRND}

Ale to nie działa. Jeśli BCKGRND nie jest ustawiony, działa - ale gdy jest ustawiony, jest interpretowany jako dosłowne „&” i błędy.

BrowncoatOkie
źródło
po użyciu końcowego echo $!
znaku handlowego

Odpowiedzi:

9

Nie można użyć zmiennej do wywołania tła, ponieważ rozwinięcie zmiennej następuje po przeanalizowaniu wiersza polecenia dla operatorów sterujących (takich jak &&i &).

Jeszcze inną opcją byłoby zawinięcie wywołań w funkcję:

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

... a następnie ustaw zmienną zgodnie z potrzebami:

$ 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
źródło
Co nie ed? +1 w każdym razie, to najczystsze rozwiązanie.
Stephen Kitt
LOL @StephenKitt; teraz biegi się zmieniają
Jeff Schaller
Podobała mi się odpowiedź eval ze względu na jej prostotę, ale moje rzeczywiste polecenie, które chciałem przedstawić w tle, było zbyt skomplikowane ze zbyt dużą liczbą zmiennych, aby wygodnie korzystać z eval. @ jeff-schaller udzielił odpowiedzi, która wskazała mi kierunek, w którym poszedłem. Zamiast funkcji wstawiłem całe polecenie do zmiennej, a następnie użyłem stylu instrukcji if do wykonania polecenia z lub bez &.
BrowncoatOkie
11

Możesz przerzucać i modyfikować „pierwszoplanowe”:

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Ustaw FOREGROUNDsię truelub opróżnić, aby uruchomić proces w tle. (Ustawienie FOREGROUNDdo trueuruchomienia w tle jest wprawdzie mylące! Odpowiednie nazwy zmiennych są pozostawione jako ćwiczenie dla czytelnika).

Stephen Kitt
źródło
4
to miłe, ale nie będzie działać w powłoce bez kontroli zadań (tj. dowolnego skryptu, chyba że set -mzostanie użyty).
mosvy
9

Prawdopodobnie będziesz musiał użyć eval:

eval "sleep 5" "$BCKGRND"

evalpowoduje, że powłoka ponownie ocenia podane argumenty. Literał &byłby zatem interpretowany jako &na końcu polecenia, a nie jako argument polecenia, umieszczając polecenie w tle.

Kusalananda
źródło
2
Odpowiedź zawierająca evalpowinna zawierać ostrzeżenie, z którym należy postępować ostrożnie. Zobacz np. Tę odpowiedź .
Ralf,
Nie rozumiem, na czym polega problem z "$BCKGRND"ewaluacją do pustego argumentu.
mosvy
2
@Ralf absolutnie nie ma znaczenia w tym przypadku . W eval nie ma nic specjalnego - możesz na przykład wykonywać polecenia za pomocą rozszerzeń arytmetycznych. Może powinno być w ogóle takie ostrzeżenie przed używaniem bash (lub podobnej powłoki) ;-)
mosvy
1
@Kusalananda evalpołączy swoje argumenty spacjami przed wykonaniem rzeczywistej oceny. Wystarczy spróbować: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""jest całkowicie podobny do eval foo, bez względu na to, co IFSlub czym innym.
mosvy
1
Ewaluowane polecenie powinno być w podwójnych cudzysłowach, jeśli zawiera znaki specjalne, np eval 'sleep $TIMEOUT' "$BACKGROUND". W przeciwnym razie można uzyskać podwójne rozwinięcia, jeśli zmienna rozwija się do innej zmiennej lub zawiera znaki specjalne. Również zagnieżdżanie cytatów może być trudne.
Barmar 10.01.19