Fork me on GitHub
#babashka
<
2020-10-20
>
yubrshen01:10:53

I'd like to use babashka to process a file line by line reading from standard input, but I could not produce the processed lines on the standard output. I've simplified my code to the following to figure out the problem: (ns convert.drop-bart-and-uppercase) (defn clean-location [x] x) (defn clean [lines] (->> lines (map clean-location)) ) (clean *in*) and use the following to execute: cat samples.dat | bb -i -o -f ../convert/src/convert/drop_bart_and_uppercase.clj and here is the sample.dat: Time=Thu Oct 1 15:27:15 PDT 2020, Value=75.7, Location=L16-tempmon.xxx.yyy, Device_Type=tempSensor, Value_Type=Temperature Time=Thu Oct 1 15:28:12 PDT 2020, Value=91.4, Location=a40-tc-ups, Device_Type=UPS, Value_Type=Temperature to my understanding, I'd expect it outputs the identical two lines of the content. But I see nothing. Please help me to figure out my problem. Thanks! ------------ I found the following command works as expected: < samples.dat bb -io '(load-file "/home/yshen/data/temperature-data-archive/convert/src/convert/drop_bart_and_uppercase.clj") (convert.drop-bart-and-uppercase/clean *input*)' but I find it too clumsy to load the file and then call the function

borkdude06:10:52

@yubrshen In the first piece of code you use *in* (not *input*) which is not a seq of lines, but just the stdin stream from Clojure.

yubrshen13:10:57

@borkdude thanks! I wish that I could use *input, how?*

yubrshen14:10:34

I would like to learn what is the idiomatic way to process every line of string with Babashka?

borkdude14:10:29

@yubrshen You can use *input* but this is honestly more for one-liners on the command line. For scripts you might want to use:

$ ls | bb -e "(first (line-seq (io/reader *in*)))"
"CHANGELOG.md"

borkdude14:10:30

io/reader is coming from

yubrshen14:10:57

Thanks. use/input is perfect for my need.

yubrshen17:10:38

Finally, this is what works for my need.

yubrshen17:10:31

< samples.dat bb -i -o '(->> *input* (map (fn [line] (clojure.string/replace-first line #"Location=([^.,]+)[^,]+" #(str "Location=" (clojure.string/upper-case (last %1)))))))' I can use user/**input** inside of my script file to access the stdin as list of lines, but I have not figured out how to output lines to stdout inside my script. The above one-liner works, but it's getting hard to maintain. Is there such equivalent mechanism to let babashka to help to output lines to stdout from a script?

yubrshen17:10:19

I can improve the readability but not keeping in the ecosystem of Clojure:

yubrshen17:10:42

#!/usr/bin/env bash < $1 bb -i -o '(->> *input* (map (fn [line] (clojure.string/replace-first line #"Location=([^.,]+)[^,]+" #(str "Location=" (clojure.string/upper-case (last %1)))))))'

yubrshen17:10:41

Is there any better approach, keeping my code mostly in Clojure development environment?

borkdude18:10:45

Without babashka in/output flags:

(ns my-script
  (:require [ :as io]
            [clojure.string :as str]))

(defn lines []
  (line-seq (io/reader *in*)))

(->> (lines)
     (map
      (fn [line]
        (str/replace-first line #"Location=([^.,]+)[^,]+"
                           #(str "Location=" (str/upper-case (last %1))))))
     (run! println))

borkdude18:10:54

This also works with Clojure on the JVM

yubrshen19:10:59

Yes, exactly, this is what I'm looking for to learn to have the script to run both with Clojure and Babashka. Thanks a million!

yubrshen04:10:26

@borkdude Thank you again for your help! I have been looking for the idiom to use Babashka to perform sed like streaming editing with Clojure sophistication and simpleness. Finally, I think learned the following:

(ns convert.swap-time
  (:require [ :as io]
            ))

(defn sweep
  "Sweep every line from stdio by the function,
  and output to stdout line by line."
  [f]
  (->> (line-seq (io/reader *in*))
       (map f)
       (run! println)))

(defn move_time_front [line]
  (clojure.string/replace-first
   line
   #"^(.+, )(Time=[^,]+, )(.+)$"
   "$2$1$3"
   ))

(sweep move_time_front)

borkdude13:10:22

@yubrshen If you change *in* to *input* in your top program, that should maybe work. If you want to get lines from stdin yourself, you can use (clojure.string/split-lines (slurp *in*)) or (line-seq ( *in*))

borkdude13:10:55

ah, I see. yes. *input* is only defined in the user namespace, so you have to use user/*input* in your top program

borkdude13:10:19

or get rid of the ns declaration

yubrshen14:10:51

@borkdude I see. Just use/input Thanks! I may need to have the ns namespace in order to use Clojure's test framework.