sed uncomment specfic xml comments out of a file - bash

I have a config file which may OR may not have tasks commented out. If the tasks are commented, I want to "uncomment them"!
sed -i '/<!--/d; /-->/d testfile
I have this so far but it removes all the comments in the file.
<!--Comment at the top of the file which should not be removed
-->
<!--
<task>
<tag>
<tag1>keep this content </tag1>
</tag>
</task>
-->
I only want to remove comments for task tags which will look like the above in the file (comment won't be on the same line), but keep the content inbetween. Can i use sed to do this without removing any other comments?

This might work for you (GNU sed):
sed '/^\s*<!--/!b;N;/<task>/s/.*\n//;T;:a;n;/^\s*-->/!ba;d' file
This looks for lines beginning with comments. Then reads in the next line and unless that next line contains the <task> tag it leaves them be. If the next line does contain the <task> tag it deletes the first line, then prints following lines until the closing comment tag which it then deletes.

Related

SED Ensure XML insert not inside comment

I am working with stock RHEL7/8 tools, and writing a script that will add a piece to a config file that is formatted as XML. I have run into a case where my sed statement can insert the added text inside a comment.
My current sed command gets the last existence of the tag <Program> and inserts the new tag after its closing tag </Program>.
How can I account for this possibly, but not always being inside a comment?
My script:
sed -i '0,/<Program id/s// <Program id=\"myProgram\"> <\/Program>' filepath
XML Example (displays the error inserting inside comment):
<Program id="myProgram"></Program>
<!--
<Program id="commentedOutProgram"></Program>
<Program id="newlyAddedProgram"><Program>
-->
EDIT:
This is happening at install time. I would like to add a way for some RHEL 7/8 built in tool to look in the XML file, make sure it's not in a comment, and add the new contents
Have a go with this. The usual caveats apply: It probably only works for exactly the sample you provided. Use a proper XML tool if you need a robust solution.
sed -e '/<!--/,/-->/b' \
-e '0,\%<Program id="[^"]*"></Program>%s%<Program id="myProgram"> </Program>%' filepath
Your original script seemed to have several errors, so I couldn't copy it verbatim, but this should at least give you an idea of how to modify it: add a b to skip any lines between <!-- and -->.
The % separators are just to avoid having to backslash slashes; sed allows you to use any separator you like instead of a slash, you just have to backslash the first one.
The b command jumps to a label; if the label is not specified, it jumps to the end of the script, i.e. skips the substitution part and starts over with the next line. The address expression before b selects any comment region, i.e. any lines between a line matching <!-- and a line matching -->.

Replacing placeholder with multiple lines

I have a template that looks like:
<mydata>
<tag1>
<tag2> etc.
DATAHERE
</mydata>
I want to run a query on a DB and fetch a number of records and place them one below the other in the file at the place where there is the string DATAHERE.
My records are already fetched in mydata.txt. How do i replace the single line of DATAHERE ? I do not want to hardcode the number of lines in the template to skip. DATAHERE is my only marker.
This might work for you (GNU sed):
sed -e '/DATAHERE/{r dataFile' -e 'd}' file
Focus on the line DATAHERE and read the file dataFile then delete the current line.

replace multiple key value in one line with sed [duplicate]

Quick Summary: I need to create a Bash script to change the text within a node automatically every week. The script will match the node and replace the text inside them (if this is possible)? How would I do this?
Long Summary:
I host a Minecraft server which has shops, each of which have their own .xml file in the /ShowcaseStandalone/ffs-storage/ directory. Every Sunday my server restarts and executes several commands into the terminal to reset several things. One thing that I am trying to make change is one of the shops. I am wanting to change the text in the node <itemstack> and the text in the node <price>. I am simply wanting to take text from a .txt file in a different folder, and insert it into that node. The problem is, that the text in the node will change every week. Is there any way to replace a specific line or text within two nodes using bash?
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<scs-shop usid="cac8480951254352116d5255e795006252d404d9" version="2" type="storage">
<enchantments type="string"/>
<owner type="string">Chadward27</owner>
<world type="string">Frisnuk</world>
<itemStack type="string">329:0</itemStack>
<activity type="string">BUY</activity>
<price type="double">55.0</price>
<locX type="double">487.5</locX>
<locY type="double">179.0</locY>
<locZ type="double">-1084.5</locZ>
<amount type="integer">0</amount>
<maxAmount type="integer">0</maxAmount>
<isUnlimited type="boolean">true</isUnlimited>
<nbt-storage usid="23dffac5fb2ea7cfdcf0740159e881026fde4fa4" version="2" type="storage"/>
</scs-shop>
Operating System: Linux Ubuntu 12.04
You can use xmlstarlet to edit a XML file in a shell like this :
xmlstarlet edit -L -u "/scs-shop/price[#type='double']" -v '99.66' file.xml
NOTE
"/scs-shop/price[#type='double']" is a Xpath expression
see xmlstarlet ed --help
The XML way is cool, but if you need to use normal bash tools, you can modify a line using sed. For instance:
PRICE=123
sed -i "s/\(<price.*>\)[^<>]*\(<\/price.*\)/\1$PRICE\2/" $XML_FILE_TO_MODIFY
This will replace the price with 123.
That sed command seems daunting, so let me break it down:
\(<price.*>\)[^<>]*\(<\/price.*\) is the pattern to match. \( ... \) are parenthesis for grouping. <price.*> matches the opening price tag. [^<>]* matches anything except angle brackets, and in this case will match the contents of the price tag. <\/price.* matches the end of the price tag. Forward slash is a delimiter in sed, so I escape it with a back slash.
\1$PRICE\2 is the text to replace the matched text with. \1 refers to the first matched parenthesis group, which is the opening price tag. $PRICE is the variable with the desired price in it. \2 refers to the second parenthesis group, in this case the closing tag.
I did not have the luxury of having xmlstarlet.
I found a solution though simply by doing an inline replacement;
template-parameter.xml
<ns:Parameter>
<ns:Name required="true">##-ParamName-##</ns:Name>
<ns:Value>
<ns:Text>##-ParamValue-##</ns:Text>
</ns:Value>
</ns:Parameter>
Snippet
tokenName="foo"
tokenValue="bar"
#Replace placeholders in parameter template element
myParamElement=$(cat template-parameter.xml)
myParamElement=${myParamElement//##-ParamName-##/$tokenName}
myParamElement=${myParamElement//##-ParamValue-##/$tokenValue}
Result
<ns:Parameter>
<ns:Name required="true">foo</ns:Name>
<ns:Value>
<ns:Text>bar</ns:Text>
</ns:Value>
</ns:Parameter>

search a pattern in each line and append it at the end of that line

I have a file with the following entries:
folder1/a_b.csv folder1/generated/
folder2/folder3/a_b1.csv folder12/generated/
folder4/b_c.csv folder123/generated/
folder5/d.csv folder1/new_folder/generated/
folder6/12.csv folder/anotherfolder/morefolder/evenmorefolder/generated/
I want to copy the csv file name from each line, paste them at the end of that line and append it with ".org". Hence, the changed file would look like
folder1/a_b.csv folder1/generated/a_b.csv.org
folder2/folder3/a_b1.csv folder12/generated/a_b1.csv.org
folder4/b_c.csv folder123/generated/b_c.csv.org
folder5/d.csv folder1/new_folder/generated/d.csv.org
folder6/12.csv folder/anotherfolder/morefolder/evenmorefolder/generated/12.csv.org
Basically, I am looking for a command in vim or sed using which I can search a pattern in each line and append it at the end of that line. Is it possible?
Thanks in advance.
Vim
Here's how to do this in Vim:
:%s/\([^/]*\.csv\)\( .*\)/&\1.org/
This global (:%) substitution matches the filename (characters that don't contain /, ending in .csv), and captures \(...\) it. It then matches the rest of the line, and captures that, too.
As a replacement, first keep the original match & (or \0), then append the first capture (\1) with the additional suffix.
sed
Though the regular expression syntax is somewhat different than in Vim, the identical expression can be used with sed:
sed -e 's/\([^/]*\.csv\)\( .*\)/&\1.org/' input
Alternatives
It looks like you want to do file renaming in batches. On Linux, the mmv command-line tool is well suited for that; you'll probably find many similar tools on the web, too.
This might work for you (GNU sed):
sed -r 's|/([^ ]*) .*|&\1.org|' file

how to search and replace tag text in HTML file based on text instead of line no

How do i search a particular line based on string match and replace it with another sting.
Below is the example of a html strong which i need to modify using bash script.
< link type="text/css" rel="stylesheet" href="https://teststore.xxx.com/store/downpanel.css">
change to:
< link type="text/css" rel="stylesheet" href="https://testsstore.xxx.com/store/downpanel.css">
i.e teststore with testsstore. just trying to add 's' .
I guess i need to match all the string. because downpanel.css is the one which differentiate which line to be edit with 's'.
I being said that this can be achieved by Regualar expression.. but i couldn't able to make it . any help with syntax would be highly greatful.
thanks.
jack
If you need to replace all occurrences of this link, just do
sed 's_"https://teststore.xxx.com/store/downpanel.css"_"https://testsstore.xxx.com/store/downpanel.css"_g' old_file > new_file
If you really need to match the whole part you show, then put in in the sed command. Beware of line breaks, they will spoil the match if encountered somewhere in the middle.
Here's a reference for sed. Or just type man sed on Linux.
To solve problems like this you generally use a sed construct like this:
sed -e '/unique pattern/s/string or pattern/replacement string/' file
The initial /unique pattern/ part constrains the substitution to lines which the pattern given. For simple cases, like yours, it may be sufficient to simply perform the substitution on all lines containing teststore, thus the unique pattern part can be omitted and the substitute becomes global:
sed -e 's/teststore/testsstore/' file
If this is a problem for you and replaces something it should not you can use the original form, perhaps like this:
sed -e '/link.*teststore/s/teststore/testsstore/' file
To help limit the impact.
Note that this will only write the modified version of file to stdout. To make the change in place, add the -i switch to sed.

Resources