«Вариабилизация» амперсанда (фоновый процесс)

9

Я хочу знать, есть ли способ поместить амперсанд в переменную и по-прежнему использовать его для отправки процесса в фоновый режим.

Это работает:

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

Но разве не здорово было бы выполнить эти пять строк одной? Вот так:

BCKGRND='&'
sleep 5 ${BCKGRND}

Но это не работает. Если BCKGRND не установлен, он работает - но когда он установлен, он интерпретируется как литерал '&' и выдает ошибки.

BrowncoatOkie
источник
после использования echo $!
конечного

Ответы:

9

Невозможно использовать переменную для фонового вызова, потому что расширение переменной происходит после анализа командной строки для управляющих операторов (таких как &&и &).

Еще один вариант - заключить вызовы в функцию:

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

... и затем установите переменную по мере необходимости:

$ 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
$
Джефф Шаллер
источник
Что нет ed? +1 в любом случае, это самое чистое решение.
Стивен Китт
LOL @StephenKitt; сейчас
Джефф Шаллер
Мне понравился ответ eval за его простоту, но моя реальная команда, которую я хотел создать, была слишком сложной со слишком большим количеством переменных, чтобы было удобно использовать eval. @ Джефф-Шаллер дал ответ, который указал мне направление, в котором я пошел. Однако вместо функции я поместил всю команду в переменную, а затем использовал его стиль оператора if для выполнения команды с или без &.
BrowncoatOkie
11

Вы можете перевернуть вещи и изменить «приоритет»:

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Установите FOREGROUNDна trueили опорожнить , чтобы запустить процесс в фоновом режиме. (Настройка FOREGROUNDдля trueзапуска в фоновом режиме, по общему признанию, сбивает с толку! Соответствующие имена переменных оставлены в качестве упражнения для читателя.)

Стивен Китт
источник
4
это хорошо, но это не будет работать в оболочке без управления заданиями (т. е. любой сценарий, если он set -mне использовался).
Мосви
9

Вам, вероятно, придется использовать eval:

eval "sleep 5" "$BCKGRND"

evalзаставляет оболочку переоценивать приведенные аргументы. &Таким образом, литерал будет интерпретироваться как &конец команды, а не как аргумент команды, помещая команду в фоновом режиме.

Кусалананда
источник
2
Содержащий ответ evalдолжен содержать предупреждение, что с этим следует обращаться осторожно. Смотрите, например, этот ответ .
Ralf
Я не понимаю, в чем проблема с "$BCKGRND"оценкой пустого аргумента.
Мосви
2
@Ralf абсолютно не имеет значения в этом случае . В eval нет ничего особенного - например, вы можете выполнять команды через арифметические расширения. Может быть, должно быть такое предупреждение против использования bash (или любой подобной оболочки) вообще ;-)
mosvy
1
@Kusalananda evalобъединит свои аргументы с пробелами, прежде чем делать фактический eval. Просто попробуйте eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""полностью похож на eval foo, независимо от того, что IFSили другое.
Мосви
1
Команда eval'ed должна быть в двойных кавычках, если она содержит какие-либо специальные символы, например eval 'sleep $TIMEOUT' "$BACKGROUND". В противном случае вы можете получить двойное расширение, если переменная раскрывается в другую переменную или содержит специальные символы. Кроме того, вложенные цитаты могут быть хитрыми.
Бармар