2

I'm writing a bash script to install Phoenix applications. I'd like to add a dependency to the deps section of my config file without relying on line numbers.

31  # Type `mix help deps` for examples and options.
32  defp deps do
33    [{:phoenix, "~> 1.2.1"},
34     {:phoenix_pubsub, "~> 1.0"},
35     {:phoenix_ecto, "~> 3.0"},
36     {:postgrex, ">= 0.0.0"},
37     {:phoenix_html, "~> 2.6"},
38     {:phoenix_live_reload, "~> 1.0", only: :dev},
39     {:gettext, "~> 0.11"},
40     {:cowboy, "~> 1.0"}]
41  end

In this case, I'd like to turn the ] in line 40 into a comma, and insert {:foo, "~> 1.0"}] below line 40. I'm assuming you can use defp deps do as a marker in a sed or awk expression, and then target the first ] that follows.

What is the best way to do this?

Mark Karavan
  • 2,654
  • 1
  • 18
  • 38
  • 1
    yup, you've got a logic to start with... here's something to help you try the command https://stackoverflow.com/questions/38972736/how-to-select-lines-between-two-patterns and then ask if you get stuck – Sundeep May 08 '17 at 07:23
  • @Sundeep As it turns out, this is the only occurrence in the config file where the pattern `}]` exists, so a simple `sed` without a look around works. However, that is outside of my original question and intent. @sat has a solid answer (albeit one for GNU sed, OSX sed is different)...if you can provide a more universal solution in awk I'll mark that as correct. – Mark Karavan May 08 '17 at 23:20

2 Answers2

3

You can use this sed:

sed '/^defp deps do/{:loop; /]$/{s/]$/,\n   {:foo, "~> 1.0"}]/g;b}; n; b loop;}' file

Test :

$ sed '/^defp deps do/{:loop; /]$/{s/]$/,\n   {:foo, "~> 1.0"}]/g;b}; n; b loop;}' file
# Type `mix help deps` for examples and options.
defp deps do
  [{:phoenix, "~> 1.2.1"},
   {:phoenix_pubsub, "~> 1.0"},
   {:phoenix_ecto, "~> 3.0"},
   {:postgrex, ">= 0.0.0"},
   {:phoenix_html, "~> 2.6"},
   {:phoenix_live_reload, "~> 1.0", only: :dev},
   {:gettext, "~> 0.11"},
   {:cowboy, "~> 1.0"},
   {:foo, "~> 1.0"}]
end

Update: ( For OSX)

  1. Save this into file.sed.
#!/bin/sed
/^defp deps do/ {
   :loop
   /]$/ {
      s/]$/,\n  {:foo, "~> 1.0"}]/g
      b
   }
   n
   b loop
}
  1. Run like this

    $ sed -f file.sed inputfile.txt
    
sat
  • 14,589
  • 7
  • 46
  • 65
1

I'd go with this command:

sed -i config.txt -e 's/\({:cowboy.*\)\]/\1,\n   {:foo, "~> 1.0"}]/g'

Explanation:

  • sed -i <filename> edits the file in-place. Before you run this, you can experiment with cat filename | sed ... just to make sure you do what you want to do.
  • sed -e ... runs the expression
  • s/.../.../g is the substitution operator in sed. It replaces the stuff found between the first pair of slashes with the other stuff in the second pair of slashes.
  • /\(:cowboy.*\)\] - this matches lines that contain {:cowboy and end with ]. It captures everything, except the closing bracket ]. I hope this matches only line 40. I don't know your full file though.
  • the replacement \1,\n {:foo, "~> 1.0"}] first prints back the captured part with \1. Then adds the newline, the necessary spaces, the new config and the bracket. I.e. exactly what you what here to see.
Tamas Rev
  • 7,008
  • 5
  • 32
  • 49
  • Useful and gets the job done. I'd like something that isn't tied to the last dependency though. Also, it seems like OSX (which I'm using) implements sed differently from other systems...my version looked like this: `sed -i '' 's/\({:cowboy.*\)\]/\1,\ {:foo, "~> 1.0"}]/g' config.txt`....(can't see in the comment, but it's a proper newline, not a `\n`) – Mark Karavan May 08 '17 at 22:30