Feed content from file in bash to an elixir function

Hi,

I have a csv type file. I want to be able to call a function from the command line, and feed it the content of this file - not the file itself.

I tried to call

mix run lib/my_module.ex $(cat tmp.txt)

having in the end of my module file :

IO.puts MyModule.my_func(System.argv) 

Content is correctly processed to the func, but System.argv being a list of strings, it lost its format and is not possible to parse correctly.

If I instead try to

mix run -e "MyModule.my_func(:args)" 

I can’t find how to feed it the content of the file, using cat or something else.

How to make it work ?

Assuming bash, the following should do the trick:

mix run lib/my_module.ex "$(cat tmp.txt)"

Then bash will send the full file content in a single arg.

But I think using stdin where a much better idea:

cat tmp.csv | mix run lib/my_module.ex
2 Likes

Or you can use a library for that:

And pass the location of the file as an argument. That should work.

Yes, that would work, but for some reason I won’t have the file itself - only its content is available to me.

Your solution to send the content as a single argument is ok, but I don’t know why, it gives me a weird error :

== Compilation error in file lib/my_module.ex ==
** (Protocol.UndefinedError) protocol String.Chars not implemented for %IO.Stream{device: #PID<0.108.0>, line_or_bytes: :line, raw: true}
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
    (elixir) lib/io.ex:553: IO.puts/2
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6 

This stdin idea is interesting ! I will dig it a little.

Wait, the BEAM turns a giant string argument into an IO Stream? Hmmm, how useful if so…

1 Like

Nah, I used {:ok, stream} = StringIO.open(file_content) to do so ^^

Oh, but you said Your solution to send the content as a single argument is ok, but I don’t know why, it gives me a weird error, lol. ^.^;

You’re right, it’s not the solution that has an issue, it’s my way to handle the code after that - I already had a working solution when parsing the file myself directly that uses Stream and I tried to adapt it.

1 Like

Hmm, if you want to use the existing stream code then you could just wrap the full-content string argument in a StringIO, although changing it to direct binary calls would be far far more efficient, but keeping it as IO means that you can choose whether to take a string or a potentially very large file. :slight_smile:

Well, as you see, trying to feed ot to StringIO.open gives that error.

== Compilation error in file lib/my_module.ex ==
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:ok, #PID<0.108.0>}
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:22: String.Chars.to_string/1
    (elixir) lib/io.ex:553: IO.puts/2
    (elixir) lib/kernel/parallel_compiler.ex:208: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

I’m dumb. Of course there is a strange bug… It’s the

IO.puts MyModule.my_func(System.argv) 

that’s responsible of it ! The code works perfectly.

Thanks again !

2 Likes