Eu tenho este código que funciona:
# Hide irrelevant errors so chrome doesn't email us in cron
if [[ $fCron == true ]] ; then
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" 2>/dev/null
else
# Get silly error messages when running from terminal
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName"
fi
Se eu tentar encurtar assim:
# Hide irrelevant errors so chrome doesn't email us in cron
local HideErrors
[[ $fCron == true ]] && HideErrors="2>/dev/null"
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" "$HideErrors"
Recebo mensagens de erro:
[0826/043058.634775:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.672587:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
[0826/043058.711640:ERROR:headless_shell.cc(597)] Open multiple tabs is only supported when remote debugging is enabled.
(... SNIP ...)
Por que um argumento codificado funciona, mas não um argumento como variável?
Edição 2:
Atualmente, obtive sucesso com a sugestão alternativa da segunda resposta:
# Redirect errors when cron is used to /dev/null to reduce emails
ErrorPipe=/dev/stderr
[[ $fCron == true ]] && ErrorPipe=/dev/null
google-chrome --headless --disable-gpu --dump-dom \
"$RobWebAddress" > "$DownloadName" 2>"$ErrorPipe"
Editar 1:
Com base na primeira resposta, devo salientar que o cabeçalho do programa já contém:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
command-line
bash
redirect
WinEunuuchs2Unix
fonte
fonte
[[ $fCron == true ]] && exec 2>/dev/null
em vezRespostas:
O motivo pelo qual você não pode causar o redirecionamento ao expandir
"$HideErrors"
é que símbolos como>
não são tratados especialmente após serem produzidos pela expansão de parâmetros . Isso é realmente muito bom, porque esses símbolos aparecem no texto que você pode querer expandir e usar literalmente.Isso vale se você citar ou não
$HideErrors
. O resultado da expansão de parâmetros está sujeito à divisão e globbing de palavras quando a expansão não é citada, mas é isso.Quanto ao que fazer sobre isso, existem várias maneiras de obter o redirecionamento condicional. Para um comando muito simples, pode ser razoável escrever o comando inteiro duas vezes, uma vez em cada ramo de uma construção -
case
ou . Isso logo se torna oneroso, no entanto, e o comando que você mostrou é certamente um caso em que isso não seria o ideal.if
else
Das abordagens que permitem que você evite se repetir , há duas que eu recomendo especialmente, porque são bastante limpas e fáceis de acertar. Você deseja usar apenas um desses, não os dois ao mesmo tempo para o mesmo comando e redirecionamento.
Armazene o comando em vez do redirecionamento. Em vez de tentar armazenar o redirecionamento em uma variável e aplicar a expansão de parâmetros, armazene o comando em uma função shell . Em seguida, escreva um
case
ouif
-else
, no qual a função é chamada com o redirecionamento em uma ramificação e sem ela na outra.Se você conceituar seu comando como código que deseja escrever uma vez, mas executado sob várias circunstâncias, uma função é a solução natural. Isto é o que eu costumo fazer. Ele tem o benefício de não exigir um subconjunto nem armazenamento manual e redefinição de estado.
Com o seu código:
Você pode aplicar o espaçamento que quiser, ou
if
-else
se preferir. Observe quelaunch
usa automaticamente o chamadorRobWebAddress
e asDownloadName
variáveis, mesmo que sejam variáveis locais, porque o Bash tem escopo dinâmico , diferentemente da maioria das linguagens de programação com escopo lexical.Execute o comando em uma subshell e aplique condicionalmente o redirecionamento
exec
. Foi sobre isso que a steeldriver comentou , mas por dentro(
)
para manter o efeito local . Quando oexec
builtin é executado sem argumentos, ele não substitui o shell atual por um novo processo, mas aplica qualquer um de seus redirecionamentos ao shell atual.(Também é possível acompanhar o que era o erro padrão e restaurá-lo, sem usar um subshell e, portanto, sem sacrificar a capacidade de modificar o ambiente do shell atual. Entretanto, deixarei os detalhes disso para outras respostas.)
Com o seu código:
Após o fechamento
)
, o erro padrão é efetivamente restaurado para o que era antes, porque está realmente sendo redirecionado apenas no subshell, e não no shell pai. Isso também funciona bem com as variáveis de shell existentes, pois os subshells recebem uma cópia delas. Embora prefira usar uma função shell, admito que esse método possa exigir menos código.Ambos os métodos funcionam independentemente de qual erro padrão de arquivo ou dispositivo inicia, inclusive no caso de redirecionamentos aplicados a funções de shell que chamam o código que contém o comportamento condicional, bem como no caso (mencionado em sua edição) em que o erro padrão para o script inteiro já foi redirecionado por um ou anterior . Que o caminho foi produzido pela substituição do processo não é problema.
exec 2>&fd
exec 2> path
fonte
exec
não sei se ele está planejando uma resposta sobre isso ...exec
. Mas, como mencionei entre parênteses, não cobri aplicativos mais sofisticados nos quais o descritor de arquivos antigo é mantido e restaurado sem um subshell. Também não cobri aplicativos menos sofisticados, como manter o redirecionamento se este for o fim do script. Outra resposta, se publicada, pode abranger tanto e talvez mais.exec
que não deve afetar sua resposta.RobWebAddress
é definitivamente um contexto global.DownloadName
foi definido como local, mas deve ser um contexto global. Por alguma razão funções filhos herdam definições locais de pais (a saberDownloadName
era visível aDownloadAsHTML ()
função chamada pelaUpdateOne ()
função que definiu como locais Tem sido um dia difícil :(.Como os itens de sintaxe não são interpretados a partir dos valores variáveis expandidos. Ou seja, expansão de variável não é o mesmo que substituir a referência da variável pelo texto da variável na linha de comando. (Coisas como
;
,|
,&&
e citações etc. também não são especiais nos valores das variáveis.)O que você pode fazer é usar aliases ou usar a variável para conter apenas o destino do redirecionamento.
Os aliases são apenas uma substituição de texto, para que eles possam armazenar itens sintáticos, como operadores e palavras-chave. Em um script, você precisaria
shopt expand_aliases
, pois, por padrão, eles são desativados em shells não interativos. Então, isso imprime2
(apenas):(E você também pode
alias jos=if niin=then soj=fi
escrever todas as suas declarações if em finlandês. Tenho certeza de que qualquer pessoa que esteja lendo o script amará você.)Como alternativa, escreva sempre o redirecionamento, mas controle apenas o destino com uma variável. Você precisará de um destino não operacional para o caso em que não deseja alterar para onde vai a saída,
masNa verdade, adicionar/dev/stderr
deve funcionar nesse caso.2> /dev/stderr
não é um problema, devido à maneira como o Linux trata os fd's abertos/proc/<pid>/fd
como independentes do original. Isso afeta o posicionamento da posição de gravação e atrapalha a saída se for para um arquivo regular.Porém, ele deve funcionar no modo de acréscimo (ou se o stderr for para um tubo ou um terminal):
Então, para repetir:
2> /dev/stderr
pode quebrar.fonte
expand_aliases
é assustador, porque o seu programa pode ser mantido refém,~/.bashrc
eu acho.expand_aliases
é um pouco assustador. Mas~/.bashrc
não deve ser um problema, pois ele é lido apenas por shells interativos.profile
e os amigos que podem chamá-lo são lidos apenas por shells de login. Os shells não interativos de não login, como scripts, não devem executar nenhum deles. (Mas depois há$BASH_ENV
, e, aparentemente,.bashrc
é lido, se stdin está conectado a uma tomada de rede Como complicado ele pode ficar ....)2>> "$dst"
truque, mas acabei de perceber que não funciona no caso geral, então é melhor ter cuidado com isso.Título da pergunta: "Como passar 2> / dev / null como uma variável?" Isso pode ser feito usando
eval
Para reescrevermos como
Onde o acesso indireto à variável impede que a expansão aconteça muito cedo no restante da linha de comandos.
Aspas duplas nas variáveis funcionam bem.
fonte
DownloadName
e o texto literalRobWebAddress
é sempre usado para a URL. Você está usando$"
"
aspas . Eu acho que isso pode não ser intencional e você pode querer o$
s dentro do"
"
, mas você fez assim nos dois lugares, então não tenho certeza. Eu acho que> "$DownloadName"
deveria consertar isso. Mas entendo que você pode não gostar disso, já que misturar acidentalmente argumentos e não argumentoseval
é uma das razões pelas quais é tão perigoso e amplamente desencorajado a usareval
o comportamento concatenador.eval
concatenam antes de avaliar. Mas acho que outra maneira de dizer é que é uma maneira ofuscada de escrevereval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors"
que se assemelha à aparência do código do OP. E, em geral, usareval
para tarefas que não precisam é ruim . (Nada disso desculpas - nem mesmo explica - wrongness e hostilidade do meu velho resposta.)