"Variabilize" o "e comercial" (processo em segundo plano)

9

Quero saber se existe uma maneira de colocar o e comercial em uma variável e ainda usá-lo para enviar um processo para o plano de fundo.

Isso funciona:

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

Mas não seria legal realizar essas cinco linhas com apenas uma? Igual a:

BCKGRND='&'
sleep 5 ${BCKGRND}

Mas isso não funciona. Se BCKGRND não estiver definido, ele funcionará - mas quando estiver definido, ele será interpretado como um literal '&' e ocorrerá um erro.

BrowncoatOkie
fonte
depois de usar o e comercial final, echo $!retorna o PID
noobninja 16/01

Respostas:

9

Não é possível usar uma variável para segundo plano a chamada porque a expansão da variável ocorre após a linha de comando ser analisada pelos operadores de controle (como &&e &).

Outra opção seria agrupar as chamadas em uma função:

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

... e defina a variável conforme necessário:

$ 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
fonte
O que não ed? +1 de qualquer maneira, esta é a solução mais limpa.
Stephen Kitt
LOL @StephenKitt; agora as engrenagens estão girando
Jeff Schaller
Gostei da resposta do eval por sua simplicidade, mas o meu comando real do mundo real que eu queria ter em segundo plano era muito complicado com muitas variáveis ​​para ser confortável usando o eval. @ Jeff-Schaller deu a resposta que me apontou na direção que eu fui. Em vez de uma função, porém, coloquei o comando inteiro em uma variável e depois usei o estilo de instrução if para executar o comando com ou sem o &.
BrowncoatOkie
11

Você pode inverter as coisas e variar “em primeiro plano”:

FOREGROUND=fg
sleep 5 & ${FOREGROUND}

Defina FOREGROUNDcomo trueou vazio para executar o processo em segundo plano. (Definir FOREGROUNDpara trueexecução em segundo plano é reconhecidamente confuso! Nomes de variáveis ​​apropriados são deixados como exercício para o leitor.)

Stephen Kitt
fonte
4
isso é legal, mas não funcionará em um shell sem controle de trabalho (ou seja, qualquer script, a menos que tenha set -msido usado).
mosvy
9

Você provavelmente teria que usar eval:

eval "sleep 5" "$BCKGRND"

evalfaz com que o shell reavalie os argumentos fornecidos. Um literal &seria, portanto, interpretado como &no final de um comando e não como um argumento para o comando, colocando o comando em segundo plano.

Kusalananda
fonte
2
Uma resposta que contenha evaldeve conter um aviso de que isso deve ser tratado com cuidado. Veja, por exemplo, esta resposta .
Ralf
Não entendo qual é o problema em "$BCKGRND"avaliar um argumento vazio.
mosvy
2
@ Ralf absolutamente irrelevante neste caso . Não há nada de especial no eval - você pode executar comandos por meio de expansões aritméticas, por exemplo. Talvez deve haver uma tal advertência contra o uso de bash (ou qualquer shell similar) em tudo ;-)
mosvy
1
O @Kusalananda evalunirá seus argumentos com espaços antes de fazer a avaliação real. Basta experimentá-lo: eval printf "'{%s}\n'" foo "" "" "". eval foo "" "" "" ""é completamente semelhante eval foo, não importa o que IFSou outra coisa seja.
mosvy
1
O comando que está sendo avaliado deve estar entre aspas duplas se contiver caracteres especiais, por exemplo eval 'sleep $TIMEOUT' "$BACKGROUND". Caso contrário, você poderá obter expansões duplas se a variável se expandir para outra variável ou contiver caracteres especiais. Além disso, a cotação aninhada pode ser complicada.
Barmar