Come comporre le funzioni bash usando le pipe?


18

Ho alcune funzioni definite in questo modo:

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

Voglio comporli insieme in modo che sembrino invocazioni f | g | h.

Quale idioma dovrei usare per convertire la funzione lavorando su argomenti in argomenti di lettura da stdin? È possibile leggere coppie, tuple di argomenti dallo stream senza doverli sfuggire (ad es. Terminazione nulla)?


O vuoi qualcosa di simile h(g(f(...)))o ognuna delle funzioni legge dallo standard input ( read x; ...) e scrive nello standard output ( echo ...).
vonbrand,

Risposte:


21

Un potenziale approccio potrebbe essere quello di inserire un while...readcostrutto all'interno delle funzioni che elabori tutti i dati che sono entrati nella funzione tramite STDIN, operino su di esso e quindi emettere nuovamente i dati risultanti tramite STDOUT.

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

Occorrerà prestare attenzione a come si configurano i while ..read..componenti poiché dipenderanno fortemente dai tipi di dati che potranno consumare in modo affidabile. Potrebbe esserci una configurazione ottimale che puoi trovare.

Esempio

$ 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; }

Ecco ogni funzione da sola.

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

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Eccoli quando li usiamo insieme.

$ 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

Possono accettare vari stili di input.

#-- 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

Come addendum alla risposta di slm , ho fatto alcuni esperimenti con tuple separate da null come argomenti di funzione:

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

Note: sayTuplelegge due volte un record con terminazione null che -d $'\0'gestisce qualsiasi spazio circostante l'input IFS=. echorecord indietro circondato da-

Il risultato mostra che gestisce correttamente l'input con terminazione null contenente \ne \t:

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

Si prega di aggiungere suggerimenti per il miglioramento dei commenti, è un argomento interessante.


+1 come la tua idea. Potremmo invece inserire un ciclo all'interno che gli consentirebbe di prendere arbitrariamente # di argomenti. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm

Sembra che dovresti essere in grado di farlo IFS= read -r -d $'\0' -a argma non sono riuscito a farlo funzionare. Ciò consentirebbe la rimozione di while, il che sembra superfluo, ma è stato l'unico modello in cui sono riuscito a lavorare.
slm
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.