Problem :
I have files of the following structure:
foo.bar.baz () ->
templateUrl: require('x.jade')
I need to transform these files such that:
- They are prefixed with the line
var templateUrl = require('x.jade')
- The
templateUrl:
line is replaced withtemplateUrl: templateUrl
.
How can I do this? One option is a bash script that, for each file:
grep
s fortemplateUrl:
to get therequire
part.cat
s thevar templateUrl
line and the file together into a new file, and then replaces the original file.- Uses
sed
to replacetemplateUrl: require...
withtemplateUrl
.
But can this be done with a single sed
or awk
script that can be executed for each file?
Solution :
@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.
Input file(s):
foo.bar.baz () ->
templateUrl: require('x.jade')
perl
command:
perl -i -0777pe '$_=~s/templateUrl:( K.*)/templateUrl/;print"var templateUrl =$1n"' *
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-linep
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 regextemplateUrl: .*
, and capture the string matching the parenthesised subexpression.*
to a variable (called$1
by default)templateUrl/
replace the part after theK
marker in the matched line with texttemplateUrl
;
separates between instructionsprint"var templateUrl =$1n"
printvar 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 $an$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=" "} {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}' *