Ich habe diesen Code, der funktioniert:
# 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
Wenn ich versuche, es so zu verkürzen:
# 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"
Ich erhalte Fehlermeldungen:
[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 ...)
Warum funktioniert ein hartcodiertes Argument, aber kein Argument als Variable?
Bearbeiten 2:
Momentan fand ich Erfolg mit dem alternativen Vorschlag der zweiten Antwort:
# 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"
Bearbeiten 1:
Anhand der ersten Antwort sollte ich darauf hinweisen, dass der Programm-Header bereits Folgendes enthält:
[[ $fCron != true ]] &&
exec 2> >(grep -v 'GtkDialog mapped without a transient parent' >&2)
command-line
bash
redirect
WinEunuuchs2Unix
quelle
quelle
[[ $fCron == true ]] && exec 2>/dev/null
stattdessen versuchenAntworten:
Der Grund, warum Sie keine Umleitung durch Erweitern verursachen können,
"$HideErrors"
ist, dass Symbole wie>
nicht speziell behandelt werden, nachdem sie durch Parametererweiterung erstellt wurden . Dies ist tatsächlich sehr gut, da solche Symbole in Text enthalten sind, den Sie möglicherweise erweitern und wörtlich verwenden möchten.Dies gilt unabhängig davon, ob Sie zitieren oder nicht
$HideErrors
. Das Ergebnis der Parametererweiterung unterliegt der Wortteilung und dem Globbing, wenn die Erweiterung nicht in Anführungszeichen gesetzt ist, aber das war's.Es gibt zahlreiche Möglichkeiten, eine bedingte Umleitung zu erreichen. Für einen sehr einfachen Befehl, kann es den ganzen Befehl zweimal angemessene Schreib sein, einmal in jedem Zweig eines
case
oderif
-else
Konstrukt. Dies wird jedoch bald lästig, und der Befehl, den Sie gezeigt haben, ist sicherlich ein Fall, in dem dies nicht ideal wäre.Von den Ansätzen, mit denen Sie vermeiden können, sich zu wiederholen , sind zwei besonders zu empfehlen, da sie recht sauber und leicht zu finden sind. Sie möchten nur einen dieser Befehle verwenden, nicht beide gleichzeitig für denselben Befehl und dieselbe Umleitung.
Speichern Sie den Befehl anstelle der Umleitung. Anstatt zu versuchen, die Umleitung in einer Variablen zu speichern und die Parametererweiterung anzuwenden, speichern Sie den Befehl in einer Shell-Funktion . Dann schreiben Sie ein
case
oderif
-else
, in dem die Funktion mit der Umleitung auf einen Zweig und ohne auf den anderen Zweig aufgerufen wird.Wenn Sie Ihren Befehl als Code konzipieren, den Sie einmal schreiben, aber unter mehreren Umständen ausführen möchten, ist eine Funktion die natürliche Lösung. Das ist was ich normalerweise tue. Es hat den Vorteil, dass weder eine Unterschale noch ein manuelles Speichern und Zurücksetzen des Zustands erforderlich sind .
Mit deinem Code:
Sie können einen beliebigen Abstand verwenden oder
if
-else
wenn Sie möchten - einen anderen. Beachten Sie, dasslaunch
der AufruferRobWebAddress
und dieDownloadName
Variablen automatisch verwendet werden, auch wenn es sich um lokale Variablen handelt, da Bash im Gegensatz zu den meisten lexikalischen Programmiersprachen einen dynamischen Gültigkeitsbereich hat.Führen Sie den Befehl in einer Subshell aus und wenden Sie die Umleitung unter bestimmten Bedingungen auf an
exec
. Dies ist, was Stahlfahrer kommentierte , aber innen(
)
, um den Effekt lokal zu halten . Wenn dasexec
Builtin ohne Argumente ausgeführt wird, ersetzt es die aktuelle Shell nicht durch einen neuen Prozess, sondern wendet stattdessen eine der Umleitungen auf die aktuelle Shell an.(Es ist auch möglich, den Stand des Standardfehlers zu verfolgen und ihn wiederherzustellen, ohne eine Subshell zu verwenden und damit die Fähigkeit zu verlieren, die aktuelle Shell-Umgebung zu ändern. Die Details dazu überlasse ich jedoch anderen Antworten.)
Mit deinem Code:
Nach dem Schließen
)
wird der Standardfehler auf den vorherigen Stand zurückgesetzt, da er nur in der Subshell und nicht in der übergeordneten Shell umgeleitet wird. Auch dies funktioniert problemlos mit den vorhandenen Shell-Variablen, da Subshells eine Kopie davon erhalten. Obwohl ich lieber eine Shell-Funktion verwende, muss ich zugeben, dass für diese Methode möglicherweise weniger Code erforderlich ist.Beide Methoden funktionieren unabhängig davon, als welcher Datei- oder Gerätestandardfehler gestartet wird, einschließlich im Fall von Umleitungen, die auf Shell-Funktionen angewendet werden, die den Code aufrufen, der das bedingte Verhalten enthält, sowie im Fall (in Ihrer Bearbeitung erwähnt), in dem Standardfehler für Das gesamte Skript wurde bereits von einem vorherigen oder umgeleitet . Dass der Weg durch Prozesssubstitution entstanden ist, ist kein Problem.
exec 2>&fd
exec 2> path
quelle
exec
weiß nicht, ob er eine Antwort darauf plant ...exec
. Aber, wie ich in Klammern erwähnte, habe ich mich nicht mit anspruchsvolleren Anwendungen befasst, bei denen der alte Dateideskriptor ohne Subshell beibehalten und wiederhergestellt wird. Ich habe mich auch nicht mit weniger anspruchsvollen Anwendungen befasst, wie zum Beispiel die Weiterleitung beizubehalten, wenn dies das Ende des Skripts ist. Eine andere Antwort, wenn sie veröffentlicht wird, könnte beides und vielleicht auch mehr abdecken.exec
Meinung nach überhaupt nicht auf Ihre Antwort auswirken sollte.RobWebAddress
ist definitiv globaler Kontext.DownloadName
wurde lokal definiert, sollte aber globaler Kontext sein. Aus irgendeinem Grund erbenDownloadName
DownloadAsHTML ()
UpdateOne ()
Da Syntaxelemente nicht aus erweiterten Variablenwerten interpretiert werden. Das heißt, die Variablenerweiterung ist nicht dasselbe wie das Ersetzen der Variablenreferenz durch den Text der Variablen in der Befehlszeile. (Sachen wie
;
,|
,&&
und Zitate usw. sind auch keine speziellen in den Werten der Variablen.)Sie können Aliase verwenden oder die Variable verwenden, um nur das Ziel der Umleitung zu speichern.
Aliase sind nur ein Textersatz und können syntaktische Elemente wie Operatoren und Schlüsselwörter enthalten. In einem Skript müssen
shopt expand_aliases
Sie dies tun, da diese standardmäßig in nicht interaktiven Shells deaktiviert sind. Das druckt also2
(nur):(Und Sie könnten auch
alias jos=if niin=then soj=fi
und dann alle Ihre if-Anweisungen auf Finnisch schreiben. Ich bin sicher, dass jeder, der das Skript liest, Sie lieben würde.)Alternativ können Sie die Umleitung immer schreiben, aber nur das Ziel mit einer Variablen steuern. Sie benötigen ein No-Op-Ziel für den Fall, dass Sie nicht ändern möchten, wohin die Ausgabe geht,
aberTatsächlich ist das Hinzufügen/dev/stderr
in diesem Fall funktionieren sollte.2> /dev/stderr
kein No-Op, da Linux geöffnete FDS/proc/<pid>/fd
als unabhängig vom Original behandelt. Dies wirkt sich auf die Positionierung der Schreibposition aus und bringt die Ausgabe durcheinander, wenn sie in eine reguläre Datei verschoben wird.Es sollte jedoch im Append-Modus funktionieren (oder wenn stderr zu einer Pipe oder zu einem Terminal geht):
Also zu wiederholen:
2> /dev/stderr
kann brechen.quelle
expand_aliases
ist beängstigend, weil Ihr Programm~/.bashrc
meiner Meinung nach als Geisel gehalten werden kann .expand_aliases
ist ein bisschen gruselig. Sollte aber~/.bashrc
kein Problem sein, da es nur von interaktiven Shells gelesen wird und.profile
und Freunde, die es möglicherweise aufrufen, nur von Login-Shells gelesen werden. Nicht interaktive, nicht angemeldete Shells wie Skripte sollten keine dieser Shells ausführen. (Aber dann gibt es$BASH_ENV
, und anscheinend.bashrc
wird gelesen, ob stdin an einen Netzwerk-Socket angeschlossen ist. Wie2>> "$dst"
Trick, aber mir ist nur klar geworden, dass er im allgemeinen Fall nicht funktioniert, also sei besser vorsichtig damit.Fragentitel: "Wie übergebe ich 2> / dev / null als Variable?" Dies kann tatsächlich mit erfolgen
eval
So können wir als umschreiben
Wobei der indirekte Variablenzugriff verhindert, dass die Erweiterung im Rest der Befehlszeile zu früh erfolgt.
Doppelte Anführungszeichen in Variablen funktionieren einwandfrei.
quelle
DownloadName
LiteraltextRobWebAddress
wird immer für die URL verwendet. Sie verwenden$"
"
Zitate . Ich denke, das ist vielleicht unbeabsichtigt und du willst das$
s in der"
"
, aber du hast es an beiden Orten so gemacht, also bin ich mir nicht sicher. Ich denke,> "$DownloadName"
sollte es einfach beheben. Aber ich verstehe, dass Sie das vielleicht nicht mögen, da das versehentliche Vermischen von Argumenten und Nichtargumenteneval
ein Grund dafür ist, dass es so gefährlich und weitgehend entmutigt ist,eval
das Verkettungsverhalten von Argumenten zu verwenden .eval
vor der Auswertung verkettet werden. Aber ich denke, eine andere Art, es auszudrücken, ist, dass es eine verschleierte Art des Schreibens isteval 'google-chrome --headless --disable-gpu --dump-dom "$RobWebAddress" > "$DownloadName" '"$HideErrors"
, die dem Erscheinungsbild des OP-Codes ähnelt . Und im Allgemeinen ist die Verwendungeval
für Aufgaben, die nicht benötigt werden, schlecht . (Keine dieser Ausreden - noch erklärt - die Unrichtigkeit und Feindseligkeit meiner alten Antwort.)