Adding a line to the start of a file, based on other lines in the file

2

I have files of the following structure:

foo.bar.baz () ->
  templateUrl: require('x.jade')

I need to transform these files such that:

  1. They are prefixed with the line var templateUrl = require('x.jade')
  2. The templateUrl: line is replaced with templateUrl: templateUrl.

How can I do this? One option is a bash script that, for each file:

  1. greps for templateUrl: to get the require part.
  2. cats the var templateUrl line and the file together into a new file, and then replaces the original file.
  3. Uses sed to replace templateUrl: require... with templateUrl.

But can this be done with a single sed or awk script that can be executed for each file?

user88974

Posted 2017-08-01T16:02:32.470

Reputation: 123

1Excuse me if I didn't understand, see if this sed does what you want sed -n '1h;:a;n;s/require/&/;tInsert;H;ba;:Insert;H;s/^/var/;s/:/ =/;G;s/: .*/: templateUrl/;p' – Paulo – 2017-08-01T19:36:39.920

@Paulo Nice sed solution. Please post it as an answer possibly adding some explanations. – simlev – 2017-08-02T11:16:24.280

@simlev because I've been on holiday, I'm just catching up now. – user88974 – 2017-08-16T08:09:38.443

Answers

2

@simlev's perl solution really has less steps, but I'd like to present my sed command. It is more relevant because the OP already envisioned using sed and explicitly asks for a sed or awk script.

sed -n '1h;:a;n;s/require/&/;tInsert;H;ba;:Insert;H;s/^/var/;s/:/ =/;G;s/: .*/: templateUrl/;p'

Note: This command will not work if the file contains more than one line with : or require.

Explanation:

sed -n        # -n option disables automatic printing.

1 h           # h command puts the first line (address 1) in the hold space,
              # replacing any previous content.

:a            # Set a mark with label 'a'.

n             # n command prints the pattern space and loads next line of input,
              # replacing all pattern space content.
              # But it will not print because of -n option.

s/require/&/  # Test if the pattern space has the line OP wants edit.

t Insert      # If substitution was made, t command jumps to the mark with label 'Insert'.

H             # If not, continue with H command, it appends a '\n' to the hold space content
              # and then appends the pattern space to the hold space.

b a           # b (branch) command, jumps to the mark with label 'a'.

:Insert       # Exit from the 'a' loop. Here the hold space has all the lines that precede
              # the line with 'replace', and pattern space has the line with 'replace'.
H             # The line with 'replace' is appended to the hold space too.

s/^/var/      # Here sed finally edits the line, replacing the beginning with 'var' ...

s/:/ =/       # and only the first ':' with ' ='.

G             # G command appends the content of hold space to the edited pattern space,
              # here that line edited above in pattern space becomes the first line.

s/: .*/: templateUrl/    # One more substitution.

p'            # Finally, print the pattern space.

Paulo

Posted 2017-08-01T16:02:32.470

Reputation: 606

I agree with simlev edition, he cleaned the explanation adding comments indentation and improved my English. Also one can see my original answer by clicking at edited below the answer. I'm still learning how to use SuperUser interface :) – Paulo – 2017-08-04T12:43:47.960

2

Input file(s):

foo.bar.baz () ->
  templateUrl: require('x.jade')

perl command:

perl -i -0777pe '$_=~s/templateUrl:( \K.*)/templateUrl/;print"var templateUrl =$1\n"' *

Output file(s):

var templateUrl = require('x.jade')
foo.bar.baz () ->
  templateUrl: templateUrl

Breakdown:

  • perl scripting language that excels at text manipulation
  • -i edit files in-place
  • -0777 work with the file as a whole, as opposed to line-by-line
  • p print the file (in this case the file will be saved, due to the -i switch)
  • e execute the following command, as opposed to executing code that is saved in a file
  • ' start of instructions
  • $_=~s perform a substitution on the entire file ($_)
  • /templateUrl:( \K.*))/ look for a line matching the regex templateUrl: .*, and capture the string matching the parenthesised subexpression  .* to a variable (called $1 by default)
  • templateUrl/ replace the part after the \K marker in the matched line with text templateUrl
  • ; separates between instructions
  • print"var templateUrl =$1\n" print var templateUrl =, the contents of $1 and a newline
  • at this point, the rest of the file is implicitly printed, because the p switch was specified
  • ' end of instructions
  • * process every file in the current directory

There can be of course different approaches, such as:

perl -i -ne 'if ($_=~s/templateUrl:( \K.*)/templateUrl/){$a="templateUrl =$1"} {$file.=$_} END{print"var $a\n$file"}' *

AWK:
Since the question is about sed or awk, it is worth noting that either approach can be just as easily implemented in awk:

awk -i 'BEGIN {RS="\0"} {match($0,/templateUrl:( .*)/,m); gsub("templateUrl: .*","templateUrl: templateUrl"); print "var templateUrl ="m[1]$0}' *
awk -i '/templateUrl: / {a="templateUrl = "$2;gsub("templateUrl: .*","templateUrl: templateUrl")} NR==1 {file=$0} NR==2{file=file"\n"$0} END{print a"\n"file}' *

simlev

Posted 2017-08-01T16:02:32.470

Reputation: 3 184

Thank you! I wish I could accept both answers, both are very enlightening. – user88974 – 2017-08-16T08:10:28.227