เขียนโปรแกรมล้อ pipe ใน shell script

ใน shell script มัน pipe ต่อกันไปได้เรื่อย ๆ แบบนี้

cat input.txt | \
      prog1 x | \
      prog2 y | \
      prog3 z

โปรแกรมแบบนี้จะเขียนใหม่เป็น Ruby ก็จะได้ประมาณนี้ หรือจะใช้ Java ก็จะคล้าย ๆ กัน

p read_file("input.txt").prog1(:x)
    .prog2(:y)
    .prog3(:z)

พวก prog1 prog2 prog3 กลายเป็นชื่อ method ใน object ที่ดูจาก code ก็ไม่รู้ว่า object อะไร หรือกระทั่งตอนรันก็ไม่รู้ว่า prog1 ส่งข้อมูลอะไรออกมา

ถ้าเป็น shell script ก็ใส่ tee เข้าไปได้เลย

cat input.txt | \
      prog1 x | \
      prog2 y | \
      tee debug.txt | \
      prog3 z

แล้วก็ไปเปิดดูไฟล์ debug.txt ทีหลังได้ โดยที่โปรแกรมโดยรวมก็ยังทำงานปกติ

แต่ถ้าเป็น Ruby หรือ Java ก็จะกลายเป็นท่ายาก เพราะว่าใส่ tee เข้าไปใน object ก็ยากเพราะไม่รู้ว่า object ไหน class อะไร ใส่ไปแล้วจะมั่วหรือเปล่า

โปรแกรมมันก็จะออกมาประมาณนี้แทน

tmp = read_file("input.txt").prog1(:x)
        .prog2(:y)
write_file(tmp)
p tmp.prog3(:z)

ถ้าลองเปลี่ยนเป็น Clojure มันก็จะประมาณนี้

(-> (slurp "input.txt")
    (prog1 :x)
    (prog2 :y)
    (prog3 :z)
    println)

พวก prog1 prog2 prog3 ก็จะเป็นแค่ function ธรรมดาแทนที่จะเป็น method

นอกจากนั้นก็ยังเขียน tee ขึ้นมาง่าย ๆ ได้แบบนี้

(defn tee [data path]
    (spit path data)
    data)

แล้วก็เอา tee ไปแทรกได้แบบ shell script

(-> (slurp "input.txt")
    (prog1 :x)
    (prog2 :y)
    (tee "debug.txt")
    (prog3 :z)
    println)

โปรแกรมทั้งหมดผมไม่ได้ลองรันจริง ๆ นะครับ อาจจะเจ๊งได้ จาก blog นี้ผมรู้สึกว่าตอบคำถามตัวเองได้ว่าทำไมเวลาเขียน Clojure หรือ Common Lisp ในโปรแกรมที่ค่อนข้างซับซ้อนแล้วรู้สึกสะดวกกว่า Ruby เอาจริงๆ จะเขียน Ruby หรือ Java ให้คล้าย ๆ Clojure ก็คงทำได้ แต่เขียนไปก็จะถูกเรียกว่าไม่ idiomatic อันนี้ก็เป็นอีกเหตุผลนึงให้เปลี่ยนภาษาเพราะว่าภาษามันพ่วงวัฒนธรรมมาด้วย