Parametrisieren Sie verkettete Aufrufe eines Hilfsprogramms in Bash

12

Ich habe ein Black-Box-UNIX-Programm, das in einer Bash-Shell verwendet wird, das Datenspalten aus stdin liest, sie verarbeitet (einen Glättungseffekt anwendet) und dann auf stdout ausgibt. Ich benutze es von UNIX-Pipes, wie

generate | smooth | plot  

Für mehr Glättung kann ich das Glätten wiederholen, damit es von der Bash-Befehlszeile als aufgerufen wird

generate | smooth | smooth | plot   

oder auch

generate | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | plot

Das wird unübersichtlich. Ich möchte einen Bash-Wrapper so gestalten, dass er smoothseine Ausgabe smoothbeliebig oft in eine neue Instanz zurückspeist

generate | newsmooth 5 | plot

Anstatt von

generate | smooth | smooth | smooth | smooth | smooth | plot

Mein erster Versuch war ein Bash-Skript, das temporäre Dateien im aktuellen Verzeichnis generierte und löschte, das sich jedoch als hässlich erwies, wenn ich mich nicht in einem Verzeichnis mit Schreibzugriff befand, und bei Unterbrechung auch Mülldateien hinterließ.

Es gibt keine Argumente zum smoothProgramm.

Gibt es eine elegantere Möglichkeit, ein solches Programm zu "verpacken", um die Anzahl der Aufrufe zu parametrisieren?

Diane Wilbor
quelle
1
Ich hoffe, Ihr Beispiel ist ein erzwungener Fall für die Frage und kein tatsächlicher Bedarf
Arielnmz

Antworten:

18

Sie könnten es in eine rekursive Funktion einschließen:

smooth() {
  if [[ $1 -gt 1 ]]; then # add another call to function
    command smooth | smooth $(($1 - 1)) 
  else
    command smooth # no further 
  fi
}

Sie würden dies als verwenden

generate | smooth 5 | plot

das wäre äquivalent zu

generate | smooth | smooth | smooth | smooth | smooth | plot
muru
quelle
Das ist perfekt, verhält sich genau wie benötigt. Und jetzt habe ich das Schlüsselwort bash "command" kennengelernt.
Diane Wilbor
2
Dies ist übrigens derselbe Ansatz, den ich in Wie codiere ich eine beliebig lange Kette von Pipes? - und schon lange davor beim Umgang mit langen Editierlisten in xmlstarlet .
Charles Duffy
5

Wenn Sie es sich leisten können, so viele Kommas wie smoothmöglich einzugeben, können Sie die durch Kommas getrennte Klammererweiterung der Shell nutzen.

TL; DR

Die gesamte Befehlszeile für Ihren Beispielfall lautet:

generate | eval 'smooth |'{,,,,} plot

Hinweis:

  • Fügen Sie Kommas hinzu oder entfernen Sie sie, wenn Sie mehr oder weniger Wiederholungen von möchten smooth |
  • Es gibt kein |vorher, plotda dies in der letzten smooth |von der Brace Expansion produzierten Saite enthalten ist
  • Sie können auch Argumente für angeben smooth, sofern Sie sie korrekt in den angegebenen festen Teil einfügen können, der der offenen Klammer vorausgeht. Denken Sie auf jeden Fall daran, dass Sie sie für alle Wiederholungen des Befehls bereitstellen würden

Wie es funktioniert

Mit der durch Kommas getrennten geschweiften Klammer können Sie dynamisch Zeichenfolgen erstellen, die jeweils aus einem festgelegten Teil und den festgelegten variablen Teilen bestehen. Es werden so viele Zeichenfolgen a{b,c,d}erzeugt , wie variable Teile angegeben sind, wie es produziert wird ab ac ad.

Der kleine Trick dabei ist, dass die Klammererweiterung nur Kopien des festen Teils erzeugt , wenn Sie lieber eine Liste leerer variabler Teile erstellen, dh nur mit Kommas in den Klammern. Zum Beispiel:

smooth{,,,,}

wird herstellen:

smooth smooth smooth smooth smooth

Beachten Sie, dass 4 Kommas 5 smoothZeichenfolgen ergeben. So funktioniert diese Klammer-Erweiterung: Sie erzeugt Strings mit so vielen Kommas plus einem.

Natürlich benötigen Sie in Ihrem Fall auch jeweils eine |Trennung. smoothFügen Sie sie also einfach in den festen Teil ein, und achten Sie darauf, dass Sie sie richtig zitieren, damit die Shell sie nicht sofort interpretiert. Das ist:

'smooth|'{,,,,}

wird herstellen:

'smooth|' 'smooth|' 'smooth|' 'smooth|' 'smooth|'

Achten Sie darauf, dass Sie das feststehende Teil immer unmittelbar neben der offenen Stütze platzieren, dh es dürfen keine Abstände zwischen der ' und der {.

(Beachten Sie auch, dass Sie zur Bildung des festen Teils auch doppelte Anführungszeichen anstelle von einfachen Anführungszeichen verwenden können, wenn Sie Shell-Variablen im festen Teil erweitern müssen. Achten Sie nur auf das zusätzliche Escape-Zeichen, das erforderlich ist, wenn einige Shell-Sonderzeichen auftreten in doppelten Anführungszeichen).

An dieser Stelle benötigen Sie eine eval  auf diesen String angewendeten Befehl ausführen, damit die Shell ihn schließlich als den Pipeline-Befehl interpretiert, den sie haben soll.

Zusammenfassend wäre die gesamte Befehlszeile für Ihren Beispielfall also:

generate | eval 'smooth |'{,,,,} plot
LL3
quelle
1
Es bestehen erhebliche Sicherheitsbedenken, wenn dies an Orten verwendet wird, an denen der Anruf parametrisiert ist. Siehe meine Antwort zur rekursiven Bash-Funktion im Vergleich zur iterativen "eval" -String-Erstellung: Was ist besser? over on Stack Overflow.
Charles Duffy
1
@CharlesDuffy Ich stimme voll und ganz Ihren Bedenken hinsichtlich der implizierten Risiken bei der Verwendung von evalnicht vertrauenswürdigen, nicht bereinigten Zeichenfolgen zu Bewertungszwecken zu, d. H. Bei der Verwendung mit Variablen, die möglicherweise "unbekannten" Inhalt wie den von Ihnen verknüpften enthalten. Auf der anderen Seite evalkann es auch sehr praktisch sein, Befehle schnell zu "säubern", insbesondere wenn sie an der Eingabeaufforderung verwendet werden, wie es der Fall zu sein scheint, bei dem evaldie Eingabe nur aus einer vom Benutzer manuell eingegebenen Zeichenfolge besteht Person
LL3
Wie schon an anderer Stelle gesehen, kann man das immer durch eval stretwas anmaßendes & dummes ersetzen . /dev/stdin <<<str. Dies wird nicht nur einen Eindruck auf die Narren hinterlassen, es wird auch @CharlesDuffy von deinem Rücken abhalten ;-)
Pizdelect
1
@pizdelect, Sie können den vorherigen Kommentar von LL3 sorgfältig lesen - er ist ausgewogen, nuanciert und weise. (In der Tat hatte mein erster Kommentar Nuancen, die Sie zu ignorieren scheinen. "Wenn sie in Fällen verwendet werden, in denen der Aufruf parametrisiert ist", ist dies eine kritische Unterscheidung: Die Instanz von LL3 ist nicht parametrisiert, wodurch sie sicher ist.)
Charles Duffy