Bagaimana cara membuat fungsi bash menggunakan pipa?

18

Saya memiliki beberapa fungsi yang didefinisikan dalam mode ini:

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

Saya ingin menyusun mereka bersama sehingga doa akan terlihat seperti f | g | h.

Idom apa yang harus saya gunakan untuk mengonversi fungsi yang berfungsi pada argumen menjadi satu argumen pembacaan dari stdin? Apakah mungkin untuk membaca pasangan, tupel argumen dari stream tanpa perlu menghindarinya (mis. Null terminating)?

Rumca
sumber
Entah Anda menginginkan sesuatu seperti h(g(f(...)))atau masing-masing fungsi membaca dari input standar ( read x; ...) dan menulis ke output standar ( echo ...).
vonbrand

Jawaban:

21

Salah satu pendekatan potensial adalah dengan meletakkan while...readkonstruk di dalam fungsi Anda yang akan memproses data apa pun yang masuk ke fungsi melalui STDIN, beroperasi di atasnya, dan kemudian memancarkan kembali data yang dihasilkan melalui STDOUT.

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

Perawatan harus dihabiskan dengan cara Anda mengkonfigurasi while ..read..komponen Anda karena mereka akan sangat bergantung pada jenis data yang mereka dapat konsumsi dengan andal. Mungkin ada konfigurasi optimal yang bisa Anda buat.

Contoh

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

Inilah masing-masing fungsi dengan sendirinya.

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

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

Inilah mereka ketika kita menggunakannya bersama.

$ 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

Mereka dapat mengambil berbagai gaya 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
slm
sumber
4

Sebagai tambahan untuk jawaban slm , saya melakukan beberapa percobaan dengan tuple yang dipisahkan nol sebagai argumen fungsi:

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

Catatan: sayTupledua kali membaca catatan tanpa-penghentian yang -d $'\0'menangani semua ruang di sekitar input IFS=. echocatatan belakang dikelilingi oleh-

Hasil menunjukkan dengan benar menangani input nol-terminated yang berisi \ndan \t:

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

Silakan tambahkan saran untuk peningkatan komentar, ini adalah topik yang menarik.

grebneke
sumber
+1 menyukai ide Anda. Kita bisa meletakkan loop di dalamnya sebagai gantinya yang akan memungkinkan # untuk mengambil argumen. sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm
Sepertinya Anda harus bisa melakukan IFS= read -r -d $'\0' -a argtetapi saya tidak bisa melakukan ini. Ini akan memungkinkan untuk penghapusan while, yang tampaknya tidak perlu, tetapi satu-satunya pola saya bisa mulai bekerja.
slm