I want to replace value 2000 with 5000 for the fruit grapes. How to use sed to start searching from 'grapes' position and replace the first occurance
<fruits>
<fruit>
<name>apple</name>
<value>2000</value>
</fruit>
<fruit>
<name>grapes</name>
<value>2000</value>
</fruit>
<fruit>
<name>banana</name>
<value>2000</value>
</fruit>
</fruits>
I tried
sed '\,grapes, s/2000/5000/' fruits.txt
sed -i '\%<name>grapes</name>%,\%</fruit>%s%<value>2000</value>%<value>5000</value>%' fruits.xml
We use the generalized \% to avoid needing to backslash the slash in the closing tags. The s%%% doesn't need to be backslashed because sed already knows that the thing after s is the separator.
On *BSDish platforms, including MacOS, you need an explicit empty string argument to sed -i before the script itself (so sed -i '' '\%...')
This tries to be strict, but of course cannot cope with all the possible variations which are syntactically allowed in XML. If your file is always in exactly the expected format, this should work for now. One of the drawbacks is that you will get no warning when it stops working because Jenkins decides to start using whitespace inside the tags, or whatever.
Using xmlstarlet:
xmlstarlet ed -u "//fruits/fruit/name[.='grapes']/following-sibling::value" -v 5000 fruits.xml
This will update (ed -u) the xml by selecting the xpath //fruits/fruit/name where name is grapes and where the following-sibling is value, changing the content to 5000.
sed is for doing s/old/new that is all. For anything else you should be using awk:
$ awk '/grapes/{f=1} f && sub(/2000/,"5000"){f=0} 1' file
<fruits>
<fruit>
<name>apple</name>
<value>2000</value>
</fruit>
<fruit>
<name>grapes</name>
<value>5000</value>
</fruit>
<fruit>
<name>banana</name>
<value>2000</value>
</fruit>
</fruits>
Given an xml file consisting of lines like below:
<dependency field="no_change" name="test" conf="blahblah"/>
<dependency field="to_be_picked_up" name="test" conf="blahREPLACE_ME"/>
I would like to be able to identify lines where the value of field is equal to the to_be_picked_up (which can be anything apart from a specific string e.g. no_change) and replace the string REPLACE_ME with a specific string.
I have used the following command to do some line-level changes but I am not sure how I can script the logic for replacing REPLACE_ME only in lines where the value of the field can be anything apart from the to_be_picked_up and locate the change within the conf="".
sed -e 's/<dependency \(.*\)\(\.*\)>/\<dependency \1\/\>/'
Don't use sed to edit XML. Use an XML-aware tool. For example, in xsh, a tool based on libxml I happen to maintain, you can write
open file.xml ;
for //dependency[#field="to_be_picked_up"]/#conf
set . xsh:subst(., 'REPLACE_ME', 'RESULT') ;
save :b ;
sed '/field="no_change"/!s/REPLACE_ME/whatever/'
Using xmlstarlet, it would be:
xmlstarlet ed -u '//dependency[#field!="no_change"]/#conf' -x 'concat(substring-before(.,"REPLACE_ME"), "whatever", substring-after(., "REPLACE_ME"))'
How can I use the bash sed command to change this string:
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
into the following string? (only changing the 3rd line of string)
<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
NOTE 1: I don't just want to target the string 'AllowOverride None' because there are other occurrences in the file that should not be changed. I need to target the entire string starting with <Directory /var/www>
NOTE 2: I also need to overwrite the file. So, take that into account in your answer. And provide different versions for GNU/non-GNU versions of sed just in case.
Since the patterns contain slashes, use \% (for any character %) to mark the search patterns. Then use:
sed -e '\%^<Directory /var/www/>%,\%^</Directory>% s/AllowOverride None/AllowOverride All/'
The search patterns inside \%…% limit the search to lines between the matching patterns, and the { s/…/…/; } looks for the desired pattern within the range and makes the appropriate replacement.
If you don't want to restrict it to a single directory section but to all directory sections, adjust the start pattern appropriately. For example, this will match any <Directory> section:
sed -e '\%^<Directory [^>]*>%,\%^</Directory>% s/AllowOverride None/AllowOverride All/'
You can make it more selective depending on your requirements.
The simple version, relying on the AllowOverride line coming within two lines after <Directory...> and using a GNU sed extension, is this:
sed '/^<Directory/,+2 { s/AllowOverride None/AllowOverride All/g; }'
UPDATE: Here is the version not relying on any GNU extension (I tried it first, but made a typo and was surprised that it didn't work, that's why a posted the other version first):
sed '/^<Directory/,/^<\/Directory>/ { s/AllowOverride None/AllowOverride All/; }'
I realize this is not what you asked by maybe its worth not using sed?
How about a python solution? It walks directory passed as first parameter to script and replaces exactly <Directory element as you wrote it while only changing None to All and writes changes back to the file. It will also work with different indentation levels while preserving original indentation. Works on both python2 and python3.
After all i am assuming if you have sed you probably have python too.
#!/usr/bin/env python
import re
r = re.compile(r'(<Directory /var/www/>\s+Options Indexes FollowSymLinks\s+AllowOverride )None(\s+Require all granted\s+</Directory>)', re.MULTILINE)
for root, dirs, files in os.walk(sys.argv[1]):
for file_name in files:
if file_name.endswith('.conf'):
file_path = os.path.join(root, file_name)
with open(file_path) as fp:
data = r.sub(r'\1All\2', fp.read())
with open(file_path, 'w+') as fp:
fp.write(data)
Using Gnu Sed:
sed -zie 's!\(<Directory /var/www/>[^<]*AllowOverride\) None!\1 All!' ex1.txt
Option -z is for Null separated records: all the file is one record,
so just make a simple substitution.
[^<]* (multiline) regular expression respects Directory boundaries, and allows flexible format and order.
Your question is a good illustration of the mantra, don't use sed. Really, you shouldn't use any regex engine for context-free language like XML. But you can get close, maybe close enough, with awk.
#! /usr/bin/awk -f
/<Directory \/var\/www\/>/ {
line = NR
}
/ AllowOverride None/ && line + 2 == NR {
gsub( /None/, "All" )
}
{ print }
That way you don't have any fancy, nonstandard regex to read, and your code says exactly what it means: If you find "AllowOverride" 2 lines after the "Directory" line, replace it. The above regexes are both very simple (and Posix compliant) and should work with any version of awk.
Your answer is already given by this user just check here.
Some Reference
In the simplest calling of sed, it has one line of text in the pattern space, ie. 1 line of \n delimited text from the input. The single line in the pattern space has no \n... That's why your regex is not finding anything.
You can read multiple lines into the pattern-space and manipulate things surprisingly well, but with a more than normal effort.. Sed has a set of commands which allow this type of thing... Here is a link to a Command Summary for sed. It is the best one I've found, and got me rolling.
However forget the "one-liner" idea once you start using sed's micro-commands. It is useful to lay it out like a structured program until you get the feel of it... It is surprisingly simple, and equally unusual. You could think of it as the "assembler language" of text editing.
Summary: Use sed for simple things, and maybe a bit more, but in general, when it gets beyond working with a single line, most people prefer something else...
I'll let someone else suggest something else.. I'm really not sure what the best choice would be (I'd use sed, but that's because I don't know perl well enough.)
sed '/^a test$/{
$!{ N # append the next line when not on the last line
s/^a test\nPlease do not$/not a test\nBe/
# now test for a successful substitution, otherwise
#+ unpaired "a test" lines would be mis-handled
t sub-yes # branch_on_substitute (goto label :sub-yes)
:sub-not # a label (not essential; here to self document)
# if no substituion, print only the first line
P # pattern_first_line_print
D # pattern_ltrunc(line+nl)_top/cycle
:sub-yes # a label (the goto target of the 't' branch)
# fall through to final auto-pattern_print (2 lines)
}
}' alpha.txt
Here it is the same script, condensed into what is obviously harder to read and work with, but some would dubiously call a one-liner
sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt
Here is my command "cheat-sheet"
: # label
= # line_number
a # append_text_to_stdout_after_flush
b # branch_unconditional
c # range_change
d # pattern_delete_top/cycle
D # pattern_ltrunc(line+nl)_top/cycle
g # pattern=hold
G # pattern+=nl+hold
h # hold=pattern
H # hold+=nl+pattern
i # insert_text_to_stdout_now
l # pattern_list
n # pattern_flush=nextline_continue
N # pattern+=nl+nextline
p # pattern_print
P # pattern_first_line_print
q # flush_quit
r # append_file_to_stdout_after_flush
s # substitute
t # branch_on_substitute
w # append_pattern_to_file_now
x # swap_pattern_and_hold
y # transform_chars
I have a folder and sub folder that contains 2000 xml files.
Need to process all the files with BizTalk systems.
But some of the files has wrong tags
streetName Bombay Crescent /addressRegion
/streetName.
I need to you grep to find and replace the worng tags only.
I.e with the for loop.. find any xml file with wrong tag and replace it.
Only the tag "streetName" is affected. And only "addressRegion" is in the wrong place.
will like to do
grep -Po where streetName and *** /addressRegion if the condition is true
replace /addressRegion with /streetName
Thanks in Advance
The following will look for a tag <streetName> that with a matching closing tag of </addressRegion>, and will change addressRegion to streetName. It will replace all occurrences on the line. The street name must not contain any < signs, that would break the matching.
sed -e 's:\(<streetName>[^<]*\)</addressRegion>:\1</streetName>:g'
The command reads its standard input and writes standard output.
Sed -i will do the replacement in-place in all its input files:
sed -i -e 's:\(<streetName>[^<]*\)</addressRegion>:\1</streetName>:g' folder/subfolder/*.xml
Assume that we have a content xml-file:
<field name="id" id="1" type="number" default="" />
Assume that we have template file with tag:
INCLUDE_XML
We need to replace INCLUDE_XML tag to entire content from xml-file. We can try.
tpl_content=$(<tpl.xml)
xml_content=$(<cnt.xml)
xml_content="$(echo "$tpl_content" | sed "s/INCLUDE_XML/"$xml_content"/g")"
echo "$xml_content" > out.xml
The problem is unterminated 's' command cause xml-file has lot of bless characters (quotes, slashes, etc). How we can do the replacement without this care about the characters in content xml-file?
Just use sed's built-in facilities.
sed -e '/INCLUDE_XML/!b' -e 'r cnt.xml' -ed tpl.xml >out.xml
Translation: if the current input line doesn't match the regex, just continue. Otherwise, read in and print the other file, and delete the current line.