Fork me on GitHub
#clojure
<
2023-05-22
>
agorgl05:05:00

Is there an io/copy that copies line by line from src to dst?

igrishaev06:05:26

io/copy operates on input/output streams that, in turn, operate on bytes but no characters. Why would you care about lines?

agorgl06:05:41

I was trying to find out if there is a standard way to copy one line stream to another line stream

agorgl06:05:25

I am now using a loop/recur with .readLine and .write from one stream to another

igrishaev06:05:24

it looks like you're using readers but not stream. You can turn them into streams and then just io/copy

agorgl06:05:30

I'm actually trying to interact with a java.lang.Process's (a bash shell) output and input stream, by reading/writing to them my self, thats why the line by line requirement

agorgl06:05:57

More explicitly: I'm spawning a bash shell that calls a script, and I want to interact with it up to a point that I get a specific output, from then on I'll be handling the input / output programmatically

igrishaev06:05:24

well, to me, there is no need to split both input and output by lines. Just write/read from/into them, and that's it

igrishaev06:05:26

say, if the process reads a line, it will just wait until it faces the \n character

agorgl06:05:27

I suppose you mean I must change this:

(let [reader (io/reader (:err @streams))
      (loop []
        (let [line (.readLine reader)]
          (.write *err* line)
          (recur)))])
to this?
(loop []
  (.write *err* (.read (:err @streams)))
  (recur))

igrishaev06:05:39

Maybe simpler: (io/copy (.getErrorStream p) *err*) where .getErrorStream is a method which returns the error stream of the process

agorgl06:05:36

Thats what I initially did for *out* but it did not work/blocked because I assume it buffers by default 1024 bytes first?

igrishaev06:05:41

I don't think the buffer size does matter. The copy call should hang until the source is closed. It will be closed once the process is terminated.

agorgl06:05:13

But I want to stream the process output to my terminal output

agorgl06:05:26

It is for interactive processes not one shot ones

igrishaev06:05:28

A trivial solution would be to redirect the err/output channels into a file. The ProcessBuilder class has redirectError and redirectOutput methods that accepts http://java.io.File instances

agorgl06:05:07

I want to keep everything in memory as much as possible, and the problem is that If I call any redirect() method on Process, the out/err/in streams get replaced by NullStreams

igrishaev07:05:53

Sorry, I've got interrupted. Here is what you can use:

(def -pb (new ProcessBuilder ["ls" "-l" "/"]))
(def -p (.start -pb))
(def -out (.getInputStream -p))
( -out *out*)

total 9
drwxrwxr-x+ 63 root  admin  2016 May  1 09:13 Applications
drwxr-xr-x  93 root  wheel  2976 Nov 30 15:52 Library
drwxr-xr-x@  8 root  wheel   256 May 28  2020 System
drwxr-xr-x   5 root  admin   160 May 28  2020 Users
drwxr-xr-x   3 root  wheel    96 May 10 10:16 Volumes
drwxr-xr-x@ 38 root  wheel  1216 Dec 30  2020 bin
...

igrishaev07:05:43

there is a sort of mixture of terms: the getInputStream method returns the output channel, and the getOutputStream returns the input

igrishaev07:05:45

as you see, there is no need to split the output by lines

agorgl07:05:27

Yes, that is exactly what I did in the first place, but it works only for oneshot (non-interactive) processes! Try making an interactive bash instance with (def -pb (ProcessBuilder ["bash"]) and you'll see that io/copy blocks indefinitely because it waits for bash to exit first, while we want to show everything that bash spits in its stdout (and subsequently provide the relevant stdin to interact with it)

borkdude11:05:51

This doesn't work due to buffering. It might work with this solution: https://twitter.com/borkdude/status/1628786679447269377 Also more info here: https://github.com/babashka/process/discussions/102

agorgl11:05:13

Ah yes, I am reading about pty/tty s the last hour, will check this one too

dpsutton12:05:26

You can io copy a string. So you can loop on the lines and just use copy on the string to whatever destination

Ben Sless18:05:31

Regarding working with test containers: Have you found a pleasant way of bringing a container up once and only once when running integration tests? I ended up going with a global delay and I'd rather a better solution

pesterhazy18:05:34

Is this a limitation of clojure.test fixtures?

Ben Sless18:05:42

yes. Fixtures are attached to namespaces

Ben Sless18:05:19

And fixtures don't take arguments

igrishaev19:05:19

I'm not sure if it answers your question, but in still: in our case, we bootstrap docker compose only once in the background, then run tests, then stop the compose. It works fine. E.g.

docker-compose up -d ;; spawns pg/redis/kafka in background
lein test :integration
docker-compose down

👍 4
igrishaev19:05:25

during the development, I also have docker compose running in the background, so it's easy to reach all the data sources from repl

pesterhazy19:05:09

This is what we do as well

Ben Sless19:05:48

I wanted to do it from inside the REPL, I just need a single image

timo01:05:20

I usually use cli as well (either compose or plain docker) but if I wanted to use repl only I would use https://github.com/lispyclouds/contajners. There is https://github.com/javahippie/clj-test-containers as well (never used)

Ed08:05:40

I've done that in the past by checking to see if the port that I'm going to try and connect to is available, and if it isn't, try and start the process

👍 2
cddr10:05:48

What's wrong with the global delay?

Ben Sless10:05:53

Feels icky, although that's what I ended up going with for now

emccue18:05:20

Does anyone have a sort of "pre-boxed" solution for doing ETL between a few RDS instances? We are trying to move data to a new schema in a new RDS instance with some assumed downtime

johanwiren20:05:46

AWS DMS will help you with exactly that. https://aws.amazon.com/dms/