วิธีการเขียนฟังก์ชั่นทุบตีโดยใช้ท่อ?


18

ฉันมีฟังก์ชั่นบางอย่างที่กำหนดไว้ในแบบนี้:

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

ฉันต้องการที่จะประกอบมันเข้าด้วยกันเพื่อที่การภาวนาจะเป็นเช่นf | g | hนั้น

ฉันใช้สำนวนอะไรในการแปลงฟังก์ชั่นที่ทำงานกับข้อโต้แย้งเป็นหนึ่งข้อโต้แย้งการอ่านจาก stdin? เป็นไปได้ไหมที่จะอ่านคู่ tuples ของการขัดแย้งจากกระแสโดยไม่จำเป็นต้องหลบหนีพวกมัน (เช่นการยกเลิก null)?


ไม่ว่าคุณต้องการอะไรh(g(f(...)))หรือฟังก์ชั่นแต่ละอย่างอ่านจากอินพุตมาตรฐาน ( read x; ...) และเขียนไปยังเอาต์พุตมาตรฐาน ( echo ...)
vonbrand

คำตอบ:


21

วิธีการหนึ่งที่อาจเป็นไปได้คือการwhile...readสร้างโครงสร้างภายในฟังก์ชั่นของคุณซึ่งจะประมวลผลข้อมูลใด ๆ ที่เข้ามาในฟังก์ชันผ่าน STDIN ทำงานกับมันแล้วปล่อยข้อมูลผลลัพธ์ออกมาทาง STDOUT

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

จะต้องใช้ความระมัดระวังในการกำหนดค่าwhile ..read..ส่วนประกอบของคุณเนื่องจากจะขึ้นอยู่กับประเภทของข้อมูลที่พวกเขาจะสามารถใช้งานได้อย่างน่าเชื่อถือ อาจมีการกำหนดค่าที่เหมาะสมที่สุดที่คุณสามารถทำได้

ตัวอย่าง

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

นี่คือฟังก์ชั่นแต่ละอย่างด้วยตัวเอง

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

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

นี่คือเมื่อเราใช้พวกเขาด้วยกัน

$ 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

พวกเขาสามารถใช้รูปแบบการป้อนข้อมูลที่หลากหลาย

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

ในฐานะที่เป็นภาคผนวกของคำตอบของ slmฉันได้ทำการทดลองกับสิ่งอันดับ tuples ที่คั่นด้วย null เป็นอาร์กิวเมนต์ของฟังก์ชัน:

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

หมายเหตุ: sayTupleครั้งที่สองอ่านสิ้นสุดด้วย null บันทึกการจัดการพื้นที่ใดการป้อนข้อมูลโดยรอบ-d $'\0' บันทึกกลับล้อมรอบด้วยIFS=echo-

ผลแสดงให้เห็นว่ามันถูกต้องจัดการกับการป้อนข้อมูลที่สิ้นสุดด้วย null ที่มี\nและ\t:

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

กรุณาเพิ่มคำแนะนำสำหรับการปรับปรุงในความคิดเห็นมันเป็นหัวข้อที่น่าสนใจ


+1 เช่นความคิดของคุณ เราสามารถใส่ลูปเข้าไปด้านในแทนซึ่งจะยอมให้มันใช้ค่า args # โดยพลการ sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }.
slm

ดูเหมือนว่าคุณน่าจะทำได้IFS= read -r -d $'\0' -a argแต่ฉันไม่สามารถทำงานได้ สิ่งนี้จะช่วยให้การลบwhileซึ่งดูเหมือนไม่จำเป็น แต่เป็นรูปแบบเดียวที่ฉันสามารถทำงานได้
slm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.