1

I have a similar code to this. I want it to echo a valid JSON with quotes on properties so that it can be piped to another command. But while echoing, it is ripping off all the quotes.

def test_function() do
  map = %{
    "key1" => 12,
    "key2" => "value1",
  }

  json = Poison.encode!(map)
  IO.inspect(json)

  Mix.Shell.cmd(
    "echo #{json}",
    fn x -> IO.puts(x) end
  )
end

Expected

{"key2":"value1","key1":12}

Actual

{key2:value1,key1:12}
Shivam Singla
  • 2,117
  • 1
  • 10
  • 22

2 Answers2

1

You shouldn't need to rely on the OS echo command when Elixir IO already ties into STDOUT. I think you may be getting into trouble here because you are sending the result of IO.inspect AND the result of IO.puts (wrapped in the system's echo command) to STDOUT, so it's doubling things up and at a minimum, this is confusing, but it probably is invalid as well.

This works for me, given a simple github.json file containing

{
    "sha": "d25341478381063d1c76e81b3a52e0592a7c997f",
    "commit": {
        "author": {
            "name": "Stephen Dolan",
            "email": "mu@netsoc.tcd.ie",
            "date": "2013-06-22T16:30:59Z"
        }
    },
    "url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f"
}

stored alongside a parse.exs script with the following:

contents = File.read!("github.json")
map = Jason.decode!(contents)

json_str = Jason.encode!(map)
IO.puts(json_str)

This decoding and encoding is demonstrates that this is working (substitute Poison or whatever as desired).

Now, I can run the following command via mix run parse.exs and I see the expected result. I can also pipe the output as expected, e.g. to jq:

❯ mix run parse.exs | jq '.sha'
"d25341478381063d1c76e81b3a52e0592a7c997f"
Everett
  • 8,746
  • 5
  • 35
  • 49
  • I didn't get it - `You shouldn't need to rely on the OS echo command when Elixir IO already ties into STDOUT`. – Shivam Singla Mar 30 '21 at 18:33
  • How will I pipe a JSON string to a command? – Shivam Singla Mar 30 '21 at 18:33
  • I included an example of piping the output to the `jq` command (see above). `IO.puts` already writes to `STDOUT`, so you don't need to use `echo` or some other OS-specific utility. – Everett Mar 30 '21 at 18:40
  • Hm, the solution seems ok, but it does not work with my code. The above code is part of a long running application, so I can not use and pipe the output of running a script. – Shivam Singla Mar 30 '21 at 18:43
  • Do you mean you have other `IO.puts` statements in your code so you have other things being written to STDOUT? Maybe you should use `Logger` instead so you have more control over that. In lieu of that, yeah, quote your value as you posted. – Everett Mar 30 '21 at 18:50
  • No, I don't have any `puts` or `inspect` statement. They are here for just debugging. The code is part of a mix task. It creates an k8s config in an elixir map, convert to JSON and creates the api-resource using `kubectl apply -f -`. It may output some info to STDOUT using `Mix.shell().info/1`. – Shivam Singla Mar 30 '21 at 20:28
1

I had to quote JSON string in single-quotes

Mix.Shell.cmd(
  "echo '#{json}'",
  fn x -> IO.puts(x) end
)

Ref

Shivam Singla
  • 2,117
  • 1
  • 10
  • 22