Wie komponiere ich Bash-Funktionen mit Pipes?


18

Auf diese Weise habe ich einige Funktionen definiert:

function f {
  read and process $1
  ...
  echo $result
}

Ich möchte sie zusammenstellen, damit der Aufruf so aussieht f | g | h.

Welches Idiom soll ich verwenden, um eine Funktion, die mit Argumenten arbeitet, in eine Funktion zu konvertieren, die Argumente aus stdin liest? Ist es möglich, Paare, Tupel von Argumenten aus dem Stream zu lesen, ohne dass diese entkommen müssen (z. B. Null-Terminierung)?


Entweder Sie möchten so etwas h(g(f(...)))oder jede der Funktionen liest von der Standardeingabe ( read x; ...) und schreibt in die Standardausgabe ( echo ...).
Vonbrand

Antworten:


20

Ein möglicher Ansatz wäre, ein while...readKonstrukt in Ihre Funktionen einzufügen, das alle über STDIN in die Funktion eingegangenen Daten verarbeitet, verarbeitet und die resultierenden Daten dann über STDOUT wieder ausgibt.

function X {
  while read data; do
    ...process...
  done
}

Sie müssen sorgfältig darauf achten, wie Sie Ihre while ..read..Komponenten konfigurieren, da diese in hohem Maße von den Datentypen abhängen, die sie zuverlässig verarbeiten können. Möglicherweise gibt es eine optimale Konfiguration, die Sie finden können.

Beispiel

$ logF() { while read data; do echo "[F:$(date +"%D %T")] $data"; done; }
$ logG() { while read data; do echo "G:$data";                    done; }
$ logH() { while read data; do echo "H:$data";                    done; }

Hier ist jede Funktion für sich.

$ echo "hi" | logF
[F:02/07/14 20:01:11] hi

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Hier sind sie, wenn wir sie zusammen benutzen.

$ echo "hi" | logF | logG | logH
H:G:[F:02/07/14 19:58:18] hi

$ echo -e "hi\nbye" | logF | logG | logH
H:G:[F:02/07/14 19:58:22] hi
H:G:[F:02/07/14 19:58:22] bye

Sie können verschiedene Arten der Eingabe annehmen.

#-- ex. #1
$ cat <<<"some string of nonsense" | logF | logG | logH
H:G:[F:02/07/14 20:03:47] some string of nonsense

#-- ex. #2    
$ (logF | logG | logH) <<<"Here comes another string."
H:G:[F:02/07/14 20:04:46] Here comes another string.

#-- ex. #3
$ (logF | logG | logH)
Look I can even
H:G:[F:02/07/14 20:05:19] Look I can even
type to it
H:G:[F:02/07/14 20:05:23] type to it
live
H:G:[F:02/07/14 20:05:25] live
via STDIN
H:G:[F:02/07/14 20:05:29] via STDIN
..type Ctrl + D to stop..

#-- ex. #4
$ seq 5 | logF | logG | logH
H:G:[F:02/07/14 20:07:40] 1
H:G:[F:02/07/14 20:07:40] 2
H:G:[F:02/07/14 20:07:40] 3
H:G:[F:02/07/14 20:07:40] 4
H:G:[F:02/07/14 20:07:40] 5

#-- ex. #5
$ (logF | logG | logH) < <(seq 2)
H:G:[F:02/07/14 20:15:17] 1
H:G:[F:02/07/14 20:15:17] 2

4

Als Ergänzung zu slms Antwort habe ich einige Experimente mit Nullen getrennten Tupeln als Funktionsargumente durchgeführt:

$ sayTuple() { 
    IFS= read -r -d $'\0' d1
    IFS= read -r -d $'\0' d2
    echo "sayTuple: -$d1- -$d2-"
}

Anmerkungen: sayTupleLiest zweimal einen nullterminierten Datensatz-d $'\0' , der alle Leerzeichen um die Eingabe behandelt IFS=. echoDatensätze umgeben von-

Das Ergebnis zeigt, dass nullterminierte Eingaben korrekt verarbeitet werden \n , und \t:

$ printf "%s\0%s\0" "Hello " $' Brave\n\tWorld' | sayTuple 
sayTuple: -Hello - - Brave
        World-

Bitte fügen Sie Verbesserungsvorschläge in die Kommentare ein, es ist ein interessantes Thema.


+1 wie deine Idee. Wir könnten stattdessen eine Schleife einbauen, die es erlaubt, willkürliche Argumente aufzunehmen. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
SLM

Scheint, als hättest du in der Lage sein sollen, IFS= read -r -d $'\0' -a argaber ich konnte das nicht zum Laufen bringen. Dies würde das Entfernen von ermöglichen while, was unnötig erscheint, aber das einzige Muster war, das ich zur Arbeit bringen konnte.
SLM
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.