Replace value across multiple lines in bash
I'm struggling even with all the examples available on stackoverflow and google.
Basically i have the following text
/start r.start "" GAIN 0x256 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x256 0x2 10 0xA3 /end MACRO OTHER /end
and i need to read through this file searching for a name e.g. r.start which is passed as $1 and substitute e.g. 0x256 with a value that i pass as $2. There a two instance to substitute, line 2 and line 7.
Things i know:
- /start is preceded by 4 spaces
- GAIN is preceded by 6 spaces
- \r\n or \n might be present
- CODE is preceded by 8 spaces
Till now i've reached this point
pattern="N;s\s\/start r.start \"\"\n GAIN 0x\(.*\)" replacement"\/start r.start \"\"\n GAIN 0x82" sed -e "$pattern/$replacement/p" test.txt
but i get nothing. I was also able to substitute the first line but for whatever reason it pasted me the first two lines twice on top of each other
the expected value assuming the following call
./run.sh r.start 0x284569
should be
/start r.start "" GAIN 0x284569 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x284569 0x2 10 0xA3 /end MACRO OTHER /end
Here's my best shot for the second part
var="r.start" val="0x48209F82" pattern="\(CODE \"$var\" \).*\(.*\)" replacement="\1$val\2" sed "s/$pattern/$replacement/g" test.txt
The problem is that it is deleting everything after the value substitution. I can't put in \2 the following chars
EDIT:
By doing the following
var="r.start" val="0x48209F82" pattern="\(CODE \"$var\" \).*\(\s.*\s.*\s.*\)" replacement="\1$val\2" sed "s/$pattern/$replacement/g" test.txt
I get what i want but it feels a little bit dirty, how do i reduce the last part in case of a variable number of char? Can i just somehow match everything till end of line?
Replace multiline string in files, Substitute "Some\nThing" by the contents of file "new" in one or more input files perl -i -p0e 's/Some.*?thing\n/`cat new`/se' input.txt -i to change input.txt� -pi -e is your standard "replace in place" command-line sequence, and -0777 causes perl to slurp files whole. See perldoc perlrun to find out more about it.
This might work for you (GNU sed):
sed -E '/^ \/start r\.start /{:a;N;/^ \/end$/M!ba;s/^( GAIN | CODE "r\.start" )0x\S+/\10x284569/Mg}' file
Gather up lines between /start
and /end
and using pattern matching replace the desired values.
The solution may be placed in a function:
f () { sed -E '/^ \/start '"$1"' /{:a;N;/^ \/end$/M!ba;s/^( GAIN | CODE "'"$1"'" )0x\S+/\1'"$2"'/Mg}' "$3"; }
And called:
f r\\.start 0x284569 file
SED replace across multiple lines, sed reads each line in turn, so it'll never match a multiline pattern unless you nudge it in the right direction. The N command reads one line from� Assuming that you want to search for the string search through multiple files and replace it with replace, this is the one-liner: grep -RiIl 'search' | xargs sed -i 's/search/replace/g'. Let me now dissect it and take a quick look at the different tools in use.
Here is a perl way:
#!/usr/bin/perl use strict; use warnings; # retrieve the 2 parameters my $start = shift @ARGV or die "missing 1rst arg"; my $repl = shift @ARGV or die "missing 2nd arg"; # input file open my $fh_in, '<', 'file.txt' or die "$!"; # output file open my $fh_out, '>', 'output' or die "$!"; # loop through input file while(<$fh_in>) { # if we are between /start {1srt parameter} and /end if (/^ {4}\/start\h+$start/ ... /^ {4}\/end\h*$/) { # substitute 0x.... by {2nd parameter} s/^(?: {6}GAIN | {8}CODE "$start" )\K0x\w+/$repl/; } print $fh_out $_; }
In action:
cat file.txt /start other.start "" GAIN 0x256 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x256 0x2 10 0xA3 /end MACRO OTHER /end /start r.start "" GAIN 0x256 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x256 0x2 10 0xA3 /end MACRO OTHER /end ./test.pl r.start 0x123456 cat output /start other.start "" GAIN 0x256 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x256 0x2 10 0xA3 /end MACRO OTHER /end /start r.start "" GAIN 0x123456 __POSITIVE 1 FOO OTHER OTHER /start MACRO 200 CODE "r.start" 0x123456 0x2 10 0xA3 /end MACRO OTHER /end
text processing, This can be done very easily in perl : $ perl -i -p0e 's/START.*?END/ SINGLEWORD/s' file $ cat file My block of line starts from here� The author is the creator of nixCraft and a seasoned sysadmin, DevOps engineer, and a trainer for the Linux operating system/Unix shell scripting. Get the latest tutorials on SysAdmin, Linux/Unix and open source topics via RSS/XML feed or weekly email newsletter.
You want sed for this, and don't need a script:
Assuming the text is a file called the_text:
sed "s/0x256/REPLACETEXT/g" the_text
If you ant to do that with out echoing out the text then add a -i
sed -i "s/0x256/REPLACETEXT/g" the_text
It is possible to daisy chain these commands or embed them into single script, this one echoes to the standard out:
#!/bin/bash sed "s/0x256/${2}/g" $1 sed "s/0x256/${2}/g" $1
Replace a line with multiple lines in a file, Linux replace multiple lines in file. Replace multiline string in files, Substitute " Some\nThing" by the contents of file "new" in one or more input files perl -i -p0e� with recent bash shell, and assuming you do not need to traverse directories for file in *.txt do while read -r line do echo $ {line//find/replace} > temp done <"file" mv temp "$file" done
Replace multiple lines between tags using sed, But unfortunately, the command failed to replace as i want, it only work if the content between the tags are not break into multi-line. Could someone please� An unique marker (in this case ) is prepended to the start of the line and used as a method to bump-along the search throughout the length of the line. Once the marker reaches the end of the line the process is finished and is printed out the lookup table and markers being discarded. N.B.
For example to search all 3 digit numbers and replace them with the string number you would use: sed -i 's/\b[0-9]\{3\}\b/number/g' file.txt number Foo foo foo foo /bin/bash demo foobar number Another useful feature of sed is that you can use the ampersand character & which corresponds to the matched pattern. The character can be used multiple
sed -i 's/word1/word2/g' input.file. sed -i -e 's/word1/word2/g' -e 's/xx/yy/g' input.file. ## use + separator instead of / ##. sed -i 's+regex+new-text+g' file.txt. The above replace all occurrences of characters in word1 in the pattern space with the corresponding characters from word2.
Comments
- Could you please post expected output in your question and let us know then.
- @RavinderSingh13 updated
- @Luigi : Since your code did not work as expected, you should pass your code too.
- @Luigi : Since you have to look at a certain section in the file and do a lot of parsing in the lines, I wouldn't do this with bash. awk also is feasible only if you have a fixed number of fields in every line, but for instance if the parameter after
CODE
may have spaces insided the quotes, you are out of luck again. To find the best tool for the job, the starting point would be to define the exact syntax for the lines in question. - Hello the parameter after CODE has no spaces inside as it is the name of a variable basically. So it can't contain spaces. From my pov after finding CODE i just need to "skip" whatever is inside the double quotes and subtitute from 0x to a space (right before 0x2). The idea is that after i find the main block /start i dont really care of the second name let's say but still a double check would be great
- Works nicely, could you break down each token? I find it quite hard to understand
- Also what if the value is just 0x and not 0x[0-9a-fA-F] how do i set it so that it may or may not be present? Changed to 0x[0-9a-fA-F]* and worked fine
- I've added an explanation of the sed script. Hope that helps
- Maybe I haven't made myself clear, 0x256 is just a place holder, the idea is to substitute whatever value is present between 0x and a space
- 0x256 is as stated above an example (e.g.) but may vary in length as well
- @Toby Batch: This would replace 0x256 everywhere in the file and not only in the r.start section.