2

I'm attempting to use awk to do some simple templating. I have a "template" file that looks like this:

{
  "Thing": {
    "Code": [
      #include_code
    ]
  }
}

I'm using the awk program below to replace the #include_code line with the contents of a file, except with every line wrapped in double-quotes and ending the line with a comma (to make a valid JSON list in my output).

#!/usr/bin/awk -f

! /#include_code/ { print $0 }

/#include_code/ {
  while(( getline line<"test_file.js") > 0 ) {
    print "\"" line "\","
  }
}

where test_file.js is:

index.handler = (event, context) => {
    return true;
}

My problem is that I don't want to print the very last comma, but I'm not sure how to prevent that comma being printed. To be explicit here's the output:

{
  "Thing": {
    "Code": [
"index.handler = (event, context) => {",
"    return true;",
"}", <--- I don't want this comma...
    ]
  }
}

While I'd like an answer that does this with awk (since I'm trying to learn it). I'll be happy with an answer that points me to a different tool for templating that you would recommend I use instead.

1

One way to do it is, to count the number of lines at the beginning of your script. When outputting, then output a comma at the end only if your line number does not match the number of lines. How to do this is explained here.

Another approach is, not to print a comma when outputting the first entry, and to print one before the consequent entries. This can be done as follows:

 awk -F, '{if (!i)printf "\""$0"\"";else printf ",\n\""$0"\"";i=1}END{print ""}' a.in

This is definitely much cleaner.

There is also a third way. The code

 if(getline == 0)

reads the next line, and therefore tells you whether you are at the end of the file. Doing this is even cleaner than the method above, but incurs overhead from effectively reading the file twice, so I don't use it unless the second method fails (e.g., I have to process the next-to-last line differently as well).

  • Thanks, but I can't use NR in my case because it's not the last line of the main file I want to change, but the last line of the file I'm including in the middle of it. I can make use of your suggestion to print the comma before though, but I'll have to tweak your code slightly. – Thomas Ibbotson Aug 24 '17 at 10:00
  • Can you accept my answer then-- once you can get it to work? – Alex Aug 24 '17 at 14:06
  • Ok, I got it working with this code I've put in a Gist on github Although I was able to get to a solution based on your suggestion (thank you!), your answer doesn't fully answer my question, I think it would be good if you could include my solution to your answer, then I'll accept it. – Thomas Ibbotson Aug 24 '17 at 14:19
  • @ThomasIbbotson, I believe I've updated my one-line code to produce the same output as your script. Let me know if that's not the case. – Alex Aug 24 '17 at 15:41
  • sorry if I'm being awkward, maybe I'm being overly pedantic about having your solution solve my exact problem. The issue with your solution is that it uses NR which doesn't work for the problem as I stated it because it is not equal to 1 when I start printing the lines of the file I am trying to embed in the template. – Thomas Ibbotson Aug 24 '17 at 15:50
0

It may be easier to do it as:

sed 's/.*/"&"/;$!s/$/,/' test_file.js | sed '/#include_code/{
  r /dev/stdin
  d;}' template

(assuming there's only one occurrence of #include_code).

With awk, you could do it like:

awk '
  /#include_code/ {
    sep = ""
    while((getline < "test_file.js") > 0) {
      printf "%s", sep "\"" $0 "\""
      sep = ",\n"
    }
    if (sep) print ""
    next
  }
  {print}' template

You could also use a similar approach as the sed one above:

CODE='test_file.js' SED='sed '\''s/.*/"&"/;$!s/$/,/'\' "$CODE"' awk '
  /#include_code/{system(ENVIRON["SED"); next};{print}' template

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.