Shell-Programmierung, Vermeidung von Tempfiles

8

Ich schreibe oft KSH-Shell-Skripte, die dem gleichen Muster folgen:

  • (1) Abrufen der Ausgabe von einem oder mehreren Befehlen
  • (2) Formatieren Sie es mit grep | cut | awk | sed und drucken Sie es auf dem Bildschirm oder in einer Datei aus

Zu diesem Zweck speichere ich häufig die Ausgabe von (1) in einer temporären Datei und formatiere sie dann in (2) für diese Datei.

Nehmen Sie diesen Code zum Beispiel:

TMPFILE=file.tmp

# If tmpfile exists rm it.
[ -f $TMPFILE ] && rm -f $TMPFILE

for SERVICE in $(myfunc); do
    getInfo $SERVICE > $TMPFILE # Store raw output in the TMPFILE

    # I retrieve the relevant data from the TMPFILE
    SERV_NAME=$(head -1 $TMPFILE | sed -e 's/ $//')
    SERV_HOSTNAME=$(grep HOSTNAME $TMPFILE | cut -d "=" -f2)
    SERV_ARGS=$(grep Arguments $TMPFILE | cut -d ":" -f2)

    print $SERV_NAME $SEP $SERV_HOSTNAME $SEP $SERV_ARGS
    rm -f $TMPFILE #rm the TMPFILE in vue of next iteration
done

Gibt es eine Möglichkeit, mithilfe von Pipes, Umleitungen und Dingsbums zu vermeiden, dass jedes Mal eine Datei auf die Festplatte geschrieben wird?

Wenn es hilft, verwende ich ksh Version M-11/16 / 88i

Rahmu
quelle
3
Es ist eine gute Form, ALL_CAPS-Variablennamen in Shell-Skripten zu vermeiden und diesen Namespace als von der Shell reserviert zu behandeln, um zu vermeiden, dass wichtige Dinge wie PATHoder andere Shell- oder Umgebungsvariablen überlastet werden. TMPFILEmag in Ordnung sein, ist aber etwas TMPDIRBesonderes. Möchtest du wirklich diese Gratwanderung machen?
jw013
Für die Nachwelt: Eine andere Frage, die als Duplikat dieser unix.stackexchange.com/questions/63923/… markiert wurde, enthält eine Antwort mit benannten Fifo-Pipes, die auch hier verwendet werden könnte (obwohl dies wahrscheinlich nicht die beste Option ist dieser besondere Fall).
Goldlöckchen
@goldilocks: Vielleicht können wir die beiden Fragen zu einer zusammenführen. Können wir dazu einen Moderator kontaktieren?
Rahmu
@rahmu: Ich habe die andere Frage markiert. Ich denke, es liegt an den Mächten, die jetzt sind ...
Goldlöckchen

Antworten:

9

Ihr Code scheint mir ein völlig gerechtfertigtes Beispiel für die Verwendung von Tempfiles zu sein. Ich würde bleiben: bleib bei diesem Ansatz. Das einzige, was wirklich geändert werden muss, ist die Art und Weise, wie Sie das Tempfile erstellen. Verwenden Sie so etwas wie

 TMP=$(tempfile)

oder

 TMP=$(mktemp)

oder zumindest

 TMP=/tmp/myscript_$$

Auf diese Weise können Sie nicht zulassen, dass der Name leicht vorhergesagt wird (Sicherheit) und Interferenzen zwischen mehreren Instanzen des Skripts, die gleichzeitig ausgeführt werden, ausgeschlossen werden.

rozcietrzewiacz
quelle
2
Für die Variablenzuweisung sind keine Anführungszeichen erforderlich.
Glenn Jackman
1
@glenn Richtig, in diesem Fall sollten sie keinen Unterschied machen, da jeder der Befehle normalerweise eine Zeichenfolge ohne Leerzeichen erzeugt. Es ist jedoch eine gute Angewohnheit, Anführungszeichen in Fällen zu haben, in denen Sie einer Variablen eine Befehlsausgabe zuweisen. Daher werde ich darauf bestehen, dass dies so bleibt.
Rozcietrzewiacz
Die Anführungszeichen im letzten Beispiel wurden zur Unterscheidung entfernt.
Rozcietrzewiacz
3
@roz Nein, du hast den Punkt verpasst. Variablenzuweisungen in der Shell werden erkannt, bevor Erweiterungen vorgenommen werden, und die Feldaufteilung wird für Variablenzuweisungen NICHT durchgeführt. Somit var=$(echo lots of spaces); echo "$var"ist in Ordnung und sollte lots of spacesals Ausgabe produzieren. Die wirkliche Einschränkung, die niemand erwähnt hat, ist, dass die Befehlsersetzung alle nachfolgenden Zeilenumbrüche entfernt. Dies ist hier kein Problem und nur dann von Bedeutung, wenn Sie einen mktempFehler hatten , der Dateinamen mit nachgestellten Zeilenumbrüchen erstellt hat. Die übliche Umgehung, falls erforderlich, ist var=$(echo command with trailing newline; echo x); var=${var%x}.
jw013
1
@ jw013 Ja, das merke ich jetzt - nicht, als ich die Antwort vor einem Jahr schrieb. Vielen Dank für den Hinweis! (Fixing ...)
Rozcietrzewiacz
5

Sie könnten eine Variable verwenden:

info="$(getInfo $SERVICE)"
SERV_NAME="$(head -1 $TMPFILE <<<"$info" | sed -e 's/ $//')"
...

Von man ksh:

<<<word       A  short  form of here document in which word becomes the
              contents of the here-document after any parameter  expan-
              sion,  command  substitution, and arithmetic substitution
              occur.

Zu den Vorteilen gehören:

  • Aktiviert die parallele Ausführung.
  • Nach meiner Erfahrung ist dies tonnenweise schneller als temporäre Dateien. Sofern Sie nicht über so viele Daten verfügen, dass Sie diese am Ende austauschen, sollten diese um Größenordnungen schneller sein (außer bei HD-Caching-Puffern, die bei kleinen Datenmengen möglicherweise genauso schnell sind).
  • Andere Prozesse oder Benutzer können Ihre Daten nicht durcheinander bringen.
l0b0
quelle
<<< scheint in meinem ksh nicht zu existieren. Ich erhalte eine Fehlermeldung und kann sie anscheinend nicht in der Manpage finden. Ich benutze ksh88. Sind Sie sicher, dass diese Version diese Funktion haben sollte?
Rahmu
Nee; Ich glaube, ich habe nicht die richtige manSeite überprüft (es gab keine Erwähnung der Versionsnummer auf der Webseite: /)
l0b0
<<<ist bash 'hier string'. Ich glaube nicht, dass es in einer anderen Shell erscheint. (Oh, zshvielleicht ...)
Rozcietrzewiacz
2
@rozcietrzewiacz: Google für man ksh. Es wurde dort sicherlich erwähnt.
10b0
3
Ratet mal, wie bash Here-Strings und Here-Docs implementiert. sleep 3 <<<"here string" & lsof -p $! | grep 0rsleep 30251 anthony 0r REG 253,0 12 263271 /tmp/sh-thd-7256597168 (deleted)- Ja, es wird ein Tempfile verwendet.
Derobert
2

Sie haben zwei Möglichkeiten:

  1. Sie rufen die Daten einmal ab (in Ihrem Beispiel mit getInfo) und speichern sie wie Sie in einer Datei.

  2. Sie rufen die Daten jedes Mal ab und speichern sie nicht lokal, dh Sie rufen getInfojedes Mal an

Ich sehe das Problem beim Erstellen einer temporären Datei nicht, um ein erneutes Verarbeiten / Abrufen zu vermeiden.

Wenn Sie befürchten, die temporäre Datei irgendwo zu belassen, können Sie sie jederzeit traplöschen, falls das Skript beendet / unterbrochen wird

trap "rm -f $TMPFILE" EXIT HUP INT QUIT TERM

und verwenden Sie mktemp, um einen eindeutigen Dateinamen für Ihre temporäre Datei zu erstellen.

Matteo
quelle
1

Anstatt eine Datei zu generieren, erstellen Sie Shell-Zuweisungsanweisungen und werten Sie diese Ausgabe aus.

for SERVICE in $(myfunc); do
    eval $(getInfo $SERVICE |
               sed -n -e '1/\(.*\) *$/SERV_NAME="\1"/p' \
                   -e '/HOSTNAME/s/^[^=]*=\([^=]*\).*/SERV_HOSTNAME="\1"/p' \
                   -e '/Arguments/^[^:]*:\([^:]*\).*/SERV_ARGS="\1"/p')
    print $SERV_NAME $SEP $SERV_HOSTNAME $SED $SERV_ARGS
done

Oder wenn Sie nur die Informationen drucken möchten:

for SERVICE in $(myfunc); do
    getInfo $SERVICE | awk -vsep="$SEP" '
        BEGIN{OFS=sep}
        NR == 1 { sub(/ *$/,""); SERV_NAME=$0 }
        /HOSTNAME/ { split($0, HOST, /=/; SERV_HOSTNAME=HOST[2]; }
        /Arguments/ { split($0, ARGS, /:/; SERV_ARGS }
        END { print SERV_NAME, SERV_HOSTNAME, SERV_ARGS }'
done
Arcege
quelle