0

I have an xml that contains paths to 3 other xml files. Let's call the files main, cfg, config and engine. Relavant extract from main.xml:

<cfgfile>path/to/cfg/cfg.xml</cfgfile>
<enginefile>path/to/engine/engine.xml</enginefile>
<configfile>path/to/config/config.xml</configfile>

I want to replace "path/to/x" with pwd (and copy main, cfg, config and engine to pwd). For cfg alone I could do this:

sed 's%path/to/cfg/cfg.xml%"$(pwd)"/cfg.xml' source_path/main.xml > ./main.xml

To make it "simpler" I am trying to this through a loop:

S="";
for ele in "cfg" "engine" "config"; do
S=$S's%<'"$ele"'>.*</'"$ele"'>%<'"$ele"'>'"$(pwd)"'</'"$ele"'>;';
done;
echo $S

I have similar changes in the other 3 files. So, instead of typing out all the commands, loops would be better. I know I could use awk or python more easily, but just trying it out with sed. So the question is can I use generated variable S as a command in a sed oneliner (without redirecting it to a file) Something like: sed 'use $S' source_path/a.xml

tpb261
  • 255
  • 3
  • 12
  • 1
    If the words `XML` and `sed` appear in a question, then something is basically wrong. [Don't Parse XML/HTML With Regex.](https://stackoverflow.com/a/1732454/3776858) I suggest to use an XML/HTML parser (xmlstarlet, xmllint ...). – Cyrus Aug 30 '20 at 09:08
  • @Cyrus Agree with that. But the application for which I am doing this could as well have accepted command line arguments. There are no nested tags. It's more of a "tag: val" file which is why I thought an abomination might not be so ugly (here). – tpb261 Aug 30 '20 at 15:05

3 Answers3

2

@Cyrus comment is absolutely right. XML is more than you might think.

And if you know XPath you can easily use xmlstarlet together with sed.

for a in cfgfile enginefile configfile
do
    temppath="$( xmlstarlet sel -t -m "//${a}" -v "normalize-space(node())"  main.xml | sed -e "s#path/to/[^\/]\+#$( pwd )#g" )"
    tempxml="$( xmlstarlet ed -u "//${a}" -v "${temppath}" main.xml )" && echo "${tempxml}" > main.xml
done

This makes sure you do not break the XML Structure. Unfortunately there is no regex-match-replace function in XPath (as far as I know).

Marco
  • 824
  • 9
  • 13
1

Possible to execute dynamically constructed sed program with

sed -e "$S" input-file.txt > output-file.txt

Note that the loop suggested to generate the script has few typos - it will return:

s%<cfg>.*</cfg>%<cfg>PWD</cfg>;
s%<engine>.*</engine>%<engine>PWD</engine>;
s%<config>.*</config>%<config>PWD</config>;

Missing trailing '%', misspelling the tag name, not include the filename, etc. The code to generate the string should be fixed (and simplified) to:

s=""
for ele in "cfg" "engine" "config"; do
    tag=${ele}file
    s="${s}s%<$tag>.*</$tag>%<$tag>$(pwd)/$ele.xml</$tag>%;"
done;
#
sed -e "$s" ...
dash-o
  • 13,723
  • 1
  • 10
  • 37
  • This will only work in this special case. As soon as the XML-entity has an XML-attribute, a subentity, a space or many other things possible in XML, it will fail. – Marco Aug 30 '20 at 10:58
  • While I do agree with Marco and @Cyrus, I think I'll accept this answer because of the specific use case that I find myself in, and I was actually looking for the "sed -e" usage - somehow I managed to bungle in using it, and had to come here. – tpb261 Aug 30 '20 at 15:11
  • Fixed a typo - missing trailing '%' in the 's' command (and improve identification) – dash-o Aug 30 '20 at 15:21
0

This might work for you (GNU sed):

sed -En 's#(.*)path/to(.*)#echo "\1$(pwd)\2"#ep' file

Turn off implicit printing and extend regexps (-n and -E).

Substitute path/to for the pwd command which will be interpolated by echo in the string \1$(pwd)\2 where \1 and \2 are the parts of the line before and after path/to. The RHS of the substitution command is evaluated due to the e flag and then printed with the flag p.

potong
  • 55,640
  • 6
  • 51
  • 83