Wie wird Text in einem Shell-Skript sowohl auf dem Bildschirm als auch in einer Datei ausgegeben?

49

Derzeit habe ich ein Shell-Skript, das Nachrichten in eine Protokolldatei wie die folgende protokolliert:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

Bei der Ausführung dieses Skripts erfolgt keine Ausgabe auf dem Bildschirm. Da ich über Putty eine Verbindung zum Server herstelle, muss ich eine andere Verbindung öffnen und "tail -f log_file_path.log" ausführen, da ich die Ausführung nicht beenden kann Skript und ich möchte die Ausgabe in Echtzeit sehen.

Natürlich möchte ich, dass die Textnachrichten auf dem Bildschirm und in einer Datei gedruckt werden, aber ich möchte es in einer Zeile tun, nicht in zwei Zeilen, von denen eine keine Umleitung in eine Datei hat.

Wie kann man das erreichen?

Jurchiks
quelle

Antworten:

71

Das funktioniert:

command | tee -a "$log_file"

teeSpeichert Eingaben in eine Datei (wird -azum Anhängen und nicht zum Überschreiben verwendet) und kopiert die Eingabe auch in die Standardausgabe.

l0b0
quelle
8

Sie können hier-doc und verwenden. Beschaffen Sie sich ein effizientes, POSIX-freundliches allgemeines Sammlermodell.

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

Wenn Sie den heredoc öffnen, signalisieren Sie der Shell mit einem IOHERE-Eingabetoken, dass sie ihre Eingabe an den von Ihnen angegebenen Dateideskriptor umleiten soll, bis sie auf das andere Ende Ihres Begrenzertokens stößt. Ich habe mich umgesehen, aber ich habe nicht viele Beispiele für die Verwendung der fd-Umleitungsnummer gesehen, wie ich oben in Kombination mit dem heredoc-Operator gezeigt habe, obwohl deren Verwendung in den POSIX-Grundrichtlinien für Shell-Befehle klar spezifiziert ist. Die meisten Leute zeigen nur auf stdin und schießen, aber ich finde, dass Sourcing-Scriptlets auf diese Weise stdin-frei halten und die konstituierenden Apps sich nicht über blockierte I / O-Pfade beschweren können.

Der Inhalt des heredoc wird an den von Ihnen angegebenen Dateideskriptor übertragen, der wiederum als Shell-Code interpretiert und vom ausgeführt wird. eingebaut, aber nicht ohne Angabe eines bestimmten Pfades für. . Wenn der Pfad / proc / self Probleme bereitet, versuchen Sie es mit / dev / fd / n oder / proc / $$. Diese Methode funktioniert übrigens auch bei Rohren:

cat ./*.sh | . /dev/stdin

Ist wohl mindestens so unklug wie es aussieht. Sie können dies natürlich auch mit sh tun, aber der Zweck von. Ist, in der aktuellen Shell-Umgebung ausgeführt zu werden. Dies ist wahrscheinlich das, was Sie möchten, und abhängig von Ihrer Shell ist die Wahrscheinlichkeit, dass Sie mit einem Heredoc arbeiten, viel höher als mit einem anonymen Standardrohr.

Wie Sie wahrscheinlich bemerkt haben, habe ich Ihre Frage immer noch nicht beantwortet. Aber wenn Sie darüber nachdenken, bietet Ihnen der heredoc auf die gleiche Weise, wie er Ihren gesamten Code an. Überträgt, auch einen einzigen, einfachen Outpoint:

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

Daher wird die gesamte Terminal-Standardausgabe aus dem Code, der in Ihrem heredoc ausgeführt wird, weitergeleitet. Selbstverständlich und einfach von einer Pfeife abzustecken. Ich habe den ungepufferten Katzenanruf eingefügt, weil mir die aktuelle Standardausrichtung nicht klar ist, aber er ist wahrscheinlich überflüssig (mit ziemlicher Sicherheit ist er sowieso so geschrieben) und die Pipeline kann wahrscheinlich direkt am Abschlag enden.

Sie könnten auch das fehlende Backslash-Zitat im zweiten Beispiel in Frage stellen. Dieser Teil ist wichtig zu verstehen, bevor Sie einsteigen, und gibt Ihnen möglicherweise einige Ideen, wie er verwendet werden kann. Ein Heredoc-Limiter mit Anführungszeichen (bisher haben wir IOHERE und EOIN verwendet, und der erste mit einem Backslash, obwohl einfache oder doppelte Anführungszeichen denselben Zweck erfüllen würden) verhindert, dass die Shell Parametererweiterungen für die Shell ausführt Inhalt, aber ein nicht zitierter Begrenzer lässt seinen Inhalt für die Erweiterung offen. Die Konsequenzen davon, wenn Ihr Heredoc ist. Quellen sind dramatisch:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

Da ich den heredoc-Limiter nicht zitiert habe, hat die Shell den Inhalt beim Einlesen erweitert, bevor der resultierende Dateideskriptor an geliefert wurde. ausführen. Dies führte im Wesentlichen dazu, dass die Befehle zweimal analysiert wurden - die erweiterbaren sowieso. Da ich den umgekehrten Schrägstrich in Anführungszeichen für die Parametererweiterung "$ vars" gesetzt habe, hat die Shell die Deklaration beim ersten Durchlauf ignoriert und nur den umgekehrten Schrägstrich entfernt, sodass der gesamte printf-erweiterte Inhalt mit null ausgewertet werden konnte, wenn. bezog das Drehbuch im zweiten Durchgang.

Diese Funktionalität ist im Grunde genau das, was die eingebaute gefährliche Eval-Shell bieten kann, auch wenn das Zitieren in einem Heredoc viel einfacher zu handhaben ist als bei eval und ebenso gefährlich sein kann. Wenn Sie es nicht sorgfältig planen, ist es wahrscheinlich am besten, den "EOF" -Begrenzer aus Gewohnheit zu zitieren. Ich sage es nur.

EDIT: Äh, ich schaue zurück und denke, es ist ein bisschen zu viel. Wenn Sie lediglich mehrere Ausgaben zu einer Pipe verketten müssen, ist die einfachste Methode die Verwendung von a:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

Die Curlies versuchen, Inhalte in der aktuellen Shell auszuführen, während die Parens automatisch auslaufen. Trotzdem kann dir das jeder sagen und zumindest meiner Meinung nach der. Die heredoc-Lösung ist eine viel wertvollere Information, insbesondere wenn Sie verstehen möchten, wie die Shell tatsächlich funktioniert.

Habe Spaß!

mikeserv
quelle
3

Ich habe diese Antwort gefunden, als ich nach einem Installationsskript gesucht habe, um ein Installationsprotokoll zu schreiben.

Mein Skript ist bereits voll mit Echoanweisungen wie:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

Und ich wollte nicht, dass eine tee-Anweisung sie ausführt (oder ein anderes Skript, um die vorhandene mit einem tee aufzurufen), also schrieb ich Folgendes:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

Jetzt kann ich in meinem ursprünglichen Skript einfach 'echo' in 'echolog' ändern, wo ich die Ausgabe in einer Protokolldatei haben möchte.

AndruWitta
quelle