Tengo 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
Si trato de acortarlo así:
# 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"
Recibo mensajes de error:
[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 qué funciona un argumento codificado pero no un argumento como variable?
Edición 2:
Actualmente encontré el éxito con la sugerencia alternativa de la segunda respuesta:
# 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:
Basado en la primera respuesta, debo señalar que el encabezado del programa ya contiene:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
command-line
bash
redirect
WinEunuuchs2Unix
fuente
fuente
[[ $fCron == true ]] && exec 2>/dev/null
lugarRespuestas:
La razón por la que no puede causar la redirección al expandirse
"$HideErrors"
es que los símbolos como>
no son tratados especialmente después de ser producidos por la expansión de parámetros . Esto es realmente muy bueno, porque tales símbolos aparecen en el texto que tal vez desee expandir y usar literalmente.Esto es válido tanto si cotiza como si no
$HideErrors
. El resultado de la expansión de parámetros está sujeto a división de palabras y glob cuando la expansión no se cita, pero eso es todo.En cuanto a qué hacer al respecto, hay numerosas formas de lograr la redirección condicional. Para un comando muy simple, puede ser razonable escribir todo el comando dos veces, una en cada rama de un
case
oif
-else
constructo. Sin embargo, esto pronto se vuelve pesado, y el comando que mostró es ciertamente un caso en el que eso no sería ideal.De los enfoques que le permiten evitar repetirse , hay dos que recomiendo especialmente, porque son bastante limpios y fáciles de entender. Desea usar solo uno de estos, no ambos a la vez para el mismo comando y redirección.
Almacene el comando en lugar de la redirección. En lugar de intentar almacenar la redirección en una variable y aplicar la expansión de parámetros, almacene el comando en una función de shell . Luego escriba un
case
oif
-else
, en el que se llama a la función con la redirección en una rama y sin ella en la otra.Si conceptualiza su comando como código que desea escribir una vez pero se ejecuta bajo múltiples circunstancias, entonces una función es la solución natural. Esto es lo que suelo hacer. Tiene el beneficio de no requerir ni un subshell ni almacenamiento manual y restablecimiento de estado.
Con su código:
Puede aplicar el espacio que desee o
if
,else
en su lugar, si lo prefiere. Tenga en cuenta quelaunch
usa automáticamente la persona que llamaRobWebAddress
y lasDownloadName
variables, incluso si son variables locales, porque Bash tiene un alcance dinámico , a diferencia de la mayoría de los lenguajes de programación que tienen un alcance léxico.Ejecute el comando en una subshell y condicionalmente aplique la redirección a
exec
. Esto es lo que comentó Steeldriver , pero por dentro(
)
para mantener el efecto local . Cuando elexec
builtin se ejecuta sin argumentos, no reemplaza el shell actual con un nuevo proceso, sino que aplica cualquiera de sus redirecciones al shell actual.(También es posible hacer un seguimiento de qué error estándar era y restaurarlo, sin usar un subshell y, por lo tanto, sin sacrificar la capacidad de modificar el entorno actual del shell. Sin embargo, dejaré los detalles de eso a otras respuestas).
Con su código:
Después del cierre
)
, el error estándar se restaura a lo que era antes, porque solo se redirige en la subshell y no en la shell principal. Esto también funciona bien con las variables de shell existentes, ya que los subshells obtienen una copia de ellos. Aunque prefiero usar una función de shell, admito que este método puede requerir menos código.Ambos métodos funcionan independientemente de qué error estándar de archivo o dispositivo comience, incluso en el caso de redirecciones aplicadas a funciones de shell que llaman al código que contiene el comportamiento condicional, así como el caso (mencionado en su edición) donde el error estándar para todo el script ya ha sido redirigido por un anterior o . Que el camino fue producido por la sustitución del proceso no es problema
exec 2>&fd
exec 2> path
fuente
exec
no saber si él está planeando una respuesta en ese ...exec
. Pero, como mencioné entre paréntesis, no cubrí aplicaciones más sofisticadas en las que el antiguo descriptor de archivo se mantiene y restaura sin una subshell. Tampoco cubrí aplicaciones menos sofisticadas, como simplemente mantener la redirección si este es el final del script. Otra respuesta, si se publica, podría cubrir ambos, y tal vez más.exec
que no debería afectar su respuesta en absoluto, creo.RobWebAddress
es definitivamente un contexto global.DownloadName
se definió local pero debería ser contexto global. Por alguna razón, las funciones secundarias heredan las definiciones locales de los padres (a saber,DownloadName
era visible para laDownloadAsHTML ()
función llamada por laUpdateOne ()
función que la definía como local. Ha sido un día difícil :(Porque los elementos de sintaxis no se interpretan a partir de valores variables expandidos. Es decir, la expansión de la variable no es lo mismo que reemplazar la referencia de la variable con el texto de la variable en la línea de comando. (Cosas como
;
,|
,&&
y citas etc. tampoco son especial en los valores de las variables.)Lo que podría hacer es usar alias, o usar la variable para contener solo el objetivo de la redirección.
Los alias son solo un reemplazo de texto, por lo que pueden contener elementos sintácticos, como operadores y palabras clave. En un script, debería hacerlo
shopt expand_aliases
, ya que de forma predeterminada están deshabilitados en shells no interactivos. Entonces, esto imprime2
(solo):(Y también podría
alias jos=if niin=then soj=fi
y luego escribir todas sus declaraciones if en finlandés. Estoy seguro de que cualquiera que lea el guión lo amará).Alternativamente, escriba la redirección siempre, pero controle solo el objetivo con una variable. Sin
embargo, necesitará un objetivo no operativo para el caso en el que no desea cambiar el destino de la salida,/dev/stderr
perodebería funcionar en ese caso.En realidad, agregar2> /dev/stderr
no es un no-op debido a la forma en que Linux trata los fd's abiertos/proc/<pid>/fd
como independientes del original. Esto afecta el posicionamiento de la posición de escritura y desordenará la salida si va a un archivo normal.Sin embargo, debería funcionar en modo de agregado (o si stderr va a una tubería o a una terminal):
Entonces, para repetir:
2> /dev/stderr
puede romperse.fuente
expand_aliases
es aterradora porque~/.bashrc
creo que tu programa puede ser tomado como rehén .expand_aliases
da un poco de miedo. Pero~/.bashrc
no debería ser un problema, ya que solo se lee mediante shells interactivos,.profile
y los amigos que pueden llamarlo se leen solo mediante shells de inicio de sesión. Los shells no interactivos sin inicio de sesión, como los scripts, no deberían ejecutar ninguno de esos. (Pero luego está$BASH_ENV
, y al parecer.bashrc
se lee la entrada estándar si se conecta a un enchufe de la red ¿Cómo contorneado se puede poner esto ....)2>> "$dst"
truco, pero me di cuenta de que no funciona en el caso general, así que mejor ten cuidado con eso.Título de la pregunta: "¿Cómo pasar 2> / dev / null como variable?" Esto realmente se puede hacer usando
eval
Entonces podemos reescribir como
Donde el acceso variable indirecto evita que la expansión ocurra demasiado pronto en el resto de la línea de comando.
Las comillas dobles en variables funcionan bien.
fuente
DownloadName
y el texto literalRobWebAddress
siempre se usa para la URL. Estás usando$"
"
citas . Creo que esto puede ser involuntario y es posible que desee los$
s dentro"
"
, pero lo hizo de esa manera en ambos lugares, así que no estoy seguro. Creo que solo> "$DownloadName"
debería arreglarlo. Pero entiendo que puede que no le guste eso, ya que mezclar accidentalmente argumentos y no argumentoseval
es una de las razones por las que es tan peligroso y ampliamente desaconsejado usareval
el comportamiento concatenante.eval
concatenan antes de evaluar. Pero creo que otra forma de decirlo es que es una forma ofuscada de escribireval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors"
que se parece a la apariencia del código del OP. Y en general, usareval
para tareas que no lo necesitan es malo . (Ninguna de estas excusas, ni siquiera explica, la equivocación y hostilidad de mi vieja respuesta.)