Wenn Sie mit Alligatoren bis zum Hals sind, können Sie leicht vergessen, dass das Ziel darin bestand, den Sumpf zu entwässern.
- beliebtes Sprichwort
Es geht um die Frage echo
, und doch haben sich die meisten Antworten bisher darauf konzentriert, wie ein set +x
Befehl eingegeben werden kann . Es gibt eine viel einfachere und direktere Lösung:
{ echo "Message"; } 2> /dev/null
(Ich gebe zu, dass ich vielleicht nicht daran gedacht hätte, { …; } 2> /dev/null
wenn ich es in den früheren Antworten nicht gesehen hätte.)
Dies ist etwas umständlich, aber wenn Sie einen Block aufeinanderfolgender echo
Befehle haben, müssen Sie dies nicht für jeden einzeln tun:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Beachten Sie, dass Sie bei Zeilenumbrüchen keine Semikolons benötigen.
Sie können den Aufwand für die Eingabe verringern, indem Sie Kenorbs Idee verwenden,/dev/null
dauerhaft in einem nicht standardmäßigen Dateideskriptor (z. B. 3) zu
öffnen und dann 2>&3
statt 2> /dev/null
aller Zeiten zu sagen .
Die ersten vier Antworten zum Zeitpunkt des Schreibens erfordern, dass Sie jedes Mal etwas Besonderes tun (und in den meisten Fällen umständlich),
wenn Sie eine echo
. Wenn Sie wirklich möchten, dass alle echo
Befehle die Ausführungsablaufverfolgung unterdrücken (und warum nicht?), Können Sie dies global tun, ohne viel Code zu mischen. Erstens habe ich festgestellt, dass Aliase nicht verfolgt werden:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(Beachten Sie, dass die Arbeit Schnipsel folgenden Skript , wenn der shebang ist #!/bin/sh
, auch wenn /bin/sh
ein Link zu bash ist. Aber, wenn der shebang ist #!/bin/bash
, müssen Sie einen hinzufügen shopt -s expand_aliases
Befehl Aliase Arbeit in einem Skript zu erhalten.)
Also für meinen ersten Trick:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Wenn wir jetzt sagen echo "Message"
, rufen wir den Alias auf, der nicht verfolgt wird. Der Alias deaktiviert die Trace-Option und unterdrückt gleichzeitig die Trace-Nachricht aus dem set
Befehl (mithilfe der Technik, die zuerst in der Antwort von user5071535 vorgestellt wurde ). Anschließend wird der eigentliche echo
Befehl ausgeführt. Auf diese Weise erhalten wir einen ähnlichen Effekt wie bei der Antwort von user5071535, ohne den Code bei jedem echo
Befehl bearbeiten zu müssen . Der Trace-Modus bleibt jedoch deaktiviert. Wir können kein set -x
in den Alias setzen (oder zumindest nicht leicht), weil ein Alias nur erlaubt, dass ein Wort durch einen String ersetzt wird; Nach den Argumenten (z "Message"
. B. ) kann kein Teil der Alias-Zeichenfolge in den Befehl
eingefügt werden . So zum Beispiel, wenn das Skript enthält
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
die Ausgabe wäre
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
Daher müssen Sie die Trace-Option nach dem Anzeigen von Nachrichten immer noch wieder aktivieren - jedoch nur einmal nach jedem Block aufeinanderfolgender echo
Befehle:
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Es wäre schön, wenn wir die set -x
Automatik nach einem echo
- und wir können, mit etwas mehr Trick. Aber bevor ich das präsentiere, denke ich darüber nach. Das OP beginnt mit Skripten, die einen #!/bin/sh -ex
Shebang verwenden. Implizit könnte der Benutzer das x
aus dem shebang entfernen und ein Skript haben, das ohne Ausführungsverfolgung normal funktioniert. Es wäre schön, wenn wir eine Lösung entwickeln könnten, die diese Eigenschaft beibehält. Die ersten Antworten hier scheitern an dieser Eigenschaft, weil sie das Zurückverfolgen nach echo
Anweisungen bedingungslos aktivieren, ohne Rücksicht darauf, ob sie bereits aktiviert waren.
Diese Antwort erkennt das Problem auffällig nicht, da es es ersetzt echo
Ausgabe mit Trace-Ausgabe; Daher verschwinden alle Nachrichten, wenn die Ablaufverfolgung deaktiviert wird. Ich werde jetzt eine Lösung vorstellen, die die Rückverfolgung nach einer echo
Anweisung unter bestimmten Bedingungen aktiviert - nur wenn sie bereits aktiviert war. Die Herabstufung auf eine Lösung, die das bedingungslose Zurückverfolgen aktiviert, ist trivial und wird als Übung behandelt.
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
ist die Optionsliste; eine Verkettung der Buchstaben, die allen festgelegten Optionen entsprechen. Wenn beispielsweise die e
und x
-Optionen festgelegt sind, handelt $-
es sich um ein Durcheinander von Buchstaben, das e
und enthält x
. Mein neuer Alias (oben) speichert den Wert von, $-
bevor die Ablaufverfolgung deaktiviert wird. Wenn die Ablaufverfolgung deaktiviert ist, wird die Steuerung in eine Shell-Funktion umgewandelt. Diese Funktion führt die eigentliche Aktion aus echo
und prüft dann, ob die x
Option beim Aufrufen des Alias aktiviert war. Wenn die Option aktiviert war, wird sie durch die Funktion wieder aktiviert. Wenn es ausgeschaltet war, lässt die Funktion es ausgeschaltet.
Sie können die obigen sieben Zeilen (acht, wenn Sie einschließen shopt
) am Anfang des Skripts einfügen und den Rest in Ruhe lassen.
Dies würde es Ihnen erlauben
- eine der folgenden Shebang-Linien verwenden:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
oder einfach nur#! / bin / sh
und es sollte wie erwartet funktionieren.
- Code haben wie
(shebang)
befehl 1
befehl 2
befehl 3
setze -x
befehl 4
befehl 5
befehl 6
setze + x
befehl 7
befehl 8
befehl 9
und
- Die Befehle 4, 5 und 6 werden verfolgt - es sei denn, einer von ihnen ist ein
echo
. In diesem Fall wird er ausgeführt, aber nicht verfolgt. (Aber auch wenn Befehl 5 ein ist echo
, wird Befehl 6 trotzdem verfolgt.)
- Befehle 7, 8 und 9 werden nicht verfolgt. Auch wenn Befehl 8 ein ist
echo
, wird Befehl 9 immer noch nicht verfolgt.
- Die Befehle 1, 2 und 3 werden nachverfolgt (wie 4, 5 und 6) oder nicht (wie 7, 8 und 9), je nachdem, ob der Shebang beinhaltet
x
.
PS Ich habe festgestellt, dass ich auf meinem System das builtin
Schlüsselwort in meiner mittleren Antwort weglassen kann (die, die nur ein Alias für ist echo
). Das ist nicht überraschend. Bash (1) sagt, dass während der Alias-Erweiterung ...
… Ein Wort, das mit einem erweiterten Alias identisch ist, wird kein zweites Mal erweitert. Dies bedeutet , dass man kann alias ls
zu ls -F
, zum Beispiel, und bash versucht nicht rekursiv den Ersatztext zu erweitern.
Es überrascht nicht, dass die letzte Antwort (die mit echo_and_restore
) fehlschlägt, wenn das builtin
Schlüsselwort weggelassen wird 1 . Aber seltsamerweise funktioniert es, wenn ich die lösche builtin
und die Reihenfolge ändere:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 Es scheint zu undefiniertem Verhalten zu führen. Ich habe gesehen
- eine Endlosschleife (wahrscheinlich wegen unbegrenzter Rekursion),
- eine
/dev/null: Bad address
Fehlermeldung und
- ein Core-Dump.
echo +x; echo "$*"; echo -x
einem Alias zu machen, würde ich es gerne sehen.Bei InformIT habe ich eine Teillösung gefunden :
Ausgänge
Leider klingt das immer noch nach
set +x
, aber danach ist es zumindest ruhig. Es ist also zumindest eine Teillösung des Problems.Aber gibt es vielleicht einen besseren Weg, dies zu tun? :)
quelle
Auf diese Weise können Sie Ihre eigene Lösung verbessern, indem Sie die
set +x
Ausgabe entfernen:quelle
Setzen Sie es
set +x
in die Klammern, damit es nur für den örtlichen Geltungsbereich gilt.Beispielsweise:
würde ausgeben:
quelle
set +x
liege , aber ich glaube nicht, dass das Innere der Unterschale (oder die Verwendung von Unterschalen überhaupt) etwas Nützliches bewirkt. Sie können es entfernen und erhalten das gleiche Ergebnis. Es ist die Umleitung von stderr nach / dev / null, die die Arbeit des vorübergehenden "Deaktivierens" der Ablaufverfolgung erledigt ... Es scheintecho foo1 2>/dev/null
, als ob usw. genauso effektiv und lesbarer wäre.bash -x
) aktiviert und Sie leiten bereits&2
auf null um (da&3
wurde auf null umgeleitet), sodass ich nicht sicher bin, wie dieser Kommentar relevant ist. Vielleicht brauchen wir nur ein besseres Beispiel, das Ihren Standpunkt verdeutlicht, aber zumindest im vorliegenden Beispiel scheint es immer noch so, als ob es vereinfacht werden könnte, ohne irgendeinen Nutzen zu verlieren.Ich mag die umfassende und gut erläuterte Antwort von g-man und halte sie für die beste, die bisher bereitgestellt wurde. Es kümmert sich um den Kontext des Skripts und erzwingt keine Konfigurationen, wenn sie nicht benötigt werden. Wenn Sie diese Antwort also zuerst lesen, überprüfen Sie sie, denn der ganze Verdienst ist da.
In dieser Antwort fehlt jedoch ein wichtiger Teil: Die vorgeschlagene Methode funktioniert nicht für einen typischen Anwendungsfall, dh das Melden von Fehlern:
Aufgrund der Art und Weise, wie der Alias aufgebaut ist, wird dies auf erweitert
und Sie haben es erraten,
echo_and_restore
wird immer bedingungslos hingerichtet . Da dasset +x
Teil nicht ausgeführt wurde, wird auch der Inhalt dieser Funktion gedruckt.Die letzten ändern funktionieren würde auch nicht, weil in Bash, und ist linksassoziativ .
;
&&
||
&&
Ich habe eine Modifikation gefunden, die für diesen Anwendungsfall funktioniert:
Es verwendet eine Subshell (das
(...)
Teil), um alle Befehle zu gruppieren, und übergibt dann die Eingabezeichenfolge als Here String (das Ding) an stdin, das dann von gedruckt wird . Das ist optional, aber Sie wissen, " explizit ist besser als implizit ".<<<
cat -
-
Sie könnten auch
cat -
direkt ohne das Echo verwenden , aber ich mag diesen Weg, weil es mir erlaubt, andere Zeichenfolgen zur Ausgabe hinzuzufügen. Zum Beispiel benutze ich es normalerweise so:Und jetzt funktioniert es wunderbar:
quelle
Ausführungsablaufverfolgung geht zu
stderr
, filtern Sie es auf diese Weise:Einige Erklärungen, Schritt für Schritt:
stderr
wird umgeleitet ... -2>
>(…)
grep
ist der Befehl ...^
+ echo
…grep
Kehrt dann die Übereinstimmung um… --v
stdout
; wir leiten es dorthin um,stderr
wo es hingehört. ->&2
Das Problem ist (denke ich), dass diese Lösung die Streams desynchronisieren kann. Wegen der Filterung
stderr
kann es in Bezug auf etwas spät seinstdout
(wo dieecho
Ausgabe standardmäßig hingehört). Um das Problem zu beheben, können Sie zuerst die Streams verbinden, wenn Sie nichts dagegen haben, beide instdout
:Sie können eine solche Filterung in das Skript selbst einbauen, aber dieser Ansatz ist mit Sicherheit anfällig für Desynchronisation (dh er ist in meinen Tests aufgetreten: Die Ausführungsverfolgung eines Befehls wird möglicherweise nach der Ausgabe von "Direkt danach" angezeigt
echo
).Der Code sieht folgendermaßen aus:
Führen Sie es ohne Tricks aus:
Verwenden Sie erneut
> >(grep -v "^+ echo ") 2>&1
, um die Synchronisierung auf Kosten des Zusammenführens der Streams aufrechtzuerhalten.Ein anderer Ansatz. Sie erhalten eine "etwas redundante" und seltsam aussehende Ausgabe, da Ihr Terminal
stdout
und mischtstderr
. Diese beiden Ströme sind aus einem bestimmten Grund unterschiedliche Tiere. Prüfen Sie, ob die Analysestderr
nur Ihren Anforderungen entspricht. verwerfenstdout
:Wenn Sie in Ihrem Skript eine
echo
Druck-Debug- / Fehlermeldung haben, könnenstderr
Sie die Redundanz auf die oben beschriebene Weise beseitigen. Voller Befehl:Dieses Mal arbeiten wir nur mit
stderr
, sodass die Desynchronisation kein Problem mehr darstellt. Leider sehen Sie auf diese Weise weder eine Spur noch eine Ausgabeecho
dieser Ausdruckestdout
(sofern vorhanden). Wir könnten versuchen , unsere Filter wieder aufbauen Umleitung (zu erkennen>&2
) , aber wenn man sichecho foobar >&2
,echo >&2 foobar
undecho "foobar >&2"
dann werden Sie wahrscheinlich zustimmen , dass die Dinge kompliziert werden.Viel hängt von den Echos in Ihren Skripten ab. Überlegen Sie zweimal, bevor Sie einen komplexen Filter implementieren, da er möglicherweise nach hinten losgeht. Es ist besser, ein bisschen Redundanz zu haben, als versehentlich wichtige Informationen zu übersehen.
Anstatt die Ausführungsablaufverfolgung eines zu
echo
verwerfen, können wir seine Ausgabe verwerfen - und jede Ausgabe außer den Ablaufverfolgungen . Versuchen Sie Folgendes, um nur Ausführungsprotokolle zu analysieren:Narrensicher? Überlegen Sie, was passieren wird, wenn
echo "+ rm -rf --no-preserve-root /" >&2
das Drehbuch es enthält. Jemand könnte einen Herzinfarkt bekommen.Und schlussendlich…
Zum Glück gibt es
BASH_XTRACEFD
Umgebungsvariablen. Vonman bash
:Wir können es so benutzen:
Beachten Sie, dass die erste Zeile eine Subshell erzeugt. Auf diese Weise bleiben weder der Dateideskriptor noch die in der aktuellen Shell zugewiesene Variable gültig.
Dank dieser Funktion
BASH_XTRACEFD
können Sie Spuren ohne Echos und andere Ausgaben analysieren, unabhängig davon, um was es sich handelt. Es ist nicht genau das, was Sie wollten, aber meine Analyse lässt mich denken, dass dies (im Allgemeinen) der richtige Weg ist.Natürlich können Sie auch eine andere Methode anwenden, insbesondere wenn Sie Ihre Spuren analysieren
stdout
und / oderstderr
mit ihnen verknüpfen müssen. Sie müssen sich nur daran erinnern, dass es bestimmte Einschränkungen und Fallstricke gibt. Ich habe versucht, einige von ihnen zu zeigen.quelle
In einem Makefile könnten Sie das
@
Symbol verwenden.Beispiel Verwendung:
@echo 'message'
Aus diesem GNU-Dokument:
quelle
./test.sh: line 1: @echo: command not found
, aber ich benutze Bash.@
funktioniert nur, wenn Sie Makefiles wiedergeben.Redigiert am 29. Oktober 2016 nach Vorschlägen eines Moderators, dass das Original nicht genügend Informationen enthielt, um zu verstehen, was gerade passiert.
Dieser "Trick" erzeugt nur eine Zeile der Nachrichtenausgabe am Terminal, wenn xtrace aktiv ist:
Die ursprüngliche Frage ist Gibt es eine Möglichkeit -x aktiviert zu lassen, aber nur dann ausgegeben Nachricht anstelle der beiden Linien . Dies ist ein genaues Zitat aus der Frage.
Ich verstehe die Frage, wie man "set -x aktiviert lässt UND eine einzelne Zeile für eine Nachricht erzeugt"?
Zusammenfassend erfordert das OP:
Das OP erfordert nicht die Verwendung des Echobefehls . Sie führten es als Beispiel für die Nachrichtenproduktion an, indem sie die Abkürzung z. B. für Latin exempli gratia oder "zum Beispiel" verwendeten.
Ich denke "über den Tellerrand hinaus" und verzichte auf die Verwendung von Echo zur Erzeugung von Nachrichten. Dabei stelle ich fest, dass eine Zuweisungsanweisung alle Anforderungen erfüllen kann.
Ordnen Sie einer inaktiven Variablen eine Textzeichenfolge (die die Nachricht enthält) zu.
Das Hinzufügen dieser Zeile zu einem Skript ändert nichts an der Logik, erzeugt jedoch eine einzelne Zeile, wenn die Ablaufverfolgung aktiv ist: (Wenn Sie die Variable $ echo verwenden , ändern Sie einfach den Namen in eine andere nicht verwendete Variable.)
Was Sie auf dem Terminal sehen, ist nur 1 Zeile:
nicht zwei, da das OP nicht mag:
Hier ist ein Beispielskript zur Demonstration. Beachten Sie, dass die Variablensubstitution in einer Nachricht (der $ HOME-Verzeichnisname 4 Zeilen vom Ende entfernt) funktioniert, sodass Sie mit dieser Methode Variablen verfolgen können.
Und hier ist die Ausgabe vom Ausführen im Stammverzeichnis, dann im Ausgangsverzeichnis.
quelle
echo "Here are the files in this directory:" *
?