Comment composer des fonctions bash en utilisant des pipes?


18

J'ai peu de fonctions définies de cette façon:

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

Je veux les composer ensemble pour que l'invocation ressemble f | g | h.

Quel idiome dois-je utiliser pour convertir une fonction travaillant sur des arguments en un seul argument de lecture de stdin? Est-il possible de lire des paires, des tuples d'arguments à partir d'un flux sans avoir besoin de les échapper (par exemple, terminaison nulle)?


Soit vous voulez quelque chose comme, h(g(f(...)))soit chacune des fonctions lit à partir de l'entrée standard ( read x; ...) et écrit sur la sortie standard ( echo ...).
vonbrand

Réponses:


21

Une approche potentielle serait de mettre une while...readconstruction à l'intérieur de vos fonctions qui traiterait toutes les données entrées dans la fonction via STDIN, les exploiterait, puis émettrait les données résultantes via STDOUT.

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

Il faudra faire attention à la façon dont vous configurez vos while ..read..composants, car ils dépendront fortement des types de données qu'ils pourront consommer de manière fiable. Il peut y avoir une configuration optimale que vous pouvez trouver.

Exemple

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

Voici chaque fonction seule.

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

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Les voici quand nous les utilisons ensemble.

$ 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

Ils peuvent accepter différents styles d'entrée.

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

En complément à la réponse de slm , j'ai fait quelques expériences avec des tuples séparés par des null comme arguments de fonction:

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

Remarques: sayTuplelit deux fois un enregistrement terminé par un nul -d $'\0'gérant tout espace entourant l'entrée IFS=. echodossiers de retour entourés de-

Le résultat montre qu'il gère correctement les entrées à terminaison nulle contenant \net \t:

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

Veuillez ajouter des suggestions d'amélioration dans les commentaires, c'est un sujet intéressant.


+1 comme votre idée. Nous pourrions plutôt mettre une boucle à l'intérieur qui lui permettrait de prendre des # d'arguments arbitraires. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm

On dirait que vous devriez pouvoir en faire un, IFS= read -r -d $'\0' -a argmais je n'ai pas réussi à le faire fonctionner. Cela permettrait de supprimer lewhile , ce qui semble inutile, mais c'était le seul modèle que je pouvais obtenir.
slm
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.