Sed insert blank line below search string then insert string on the next line - bash

I have a sed expression in a function that I pass parameters to.
insert_after_new_line() {
if ! grep -q "${2}" "${3}"; then
sed -i "/${1}/{N;N;a ${2}}" "${3}"
fi
}
insert_after_new_line search insert textfile.txt
I am trying to have a blank line inserted below the search string and the insert string inserted after.
so
text
text
search
text
becomes
text
text
search
insert
text
but I keep getting the error
sed: -e expression #1, char 0: unmatched `{'

I tested this. works in command line
sed -i '/search/a \\ninsert' file

sed really delimiters commands by a newline. There is a ; but it does not work for all commands, mostly these which take file name as an argument. ; does not work for r R or for example a. Sed will read everything after a command, so sed interprets is like a ${2}} as a single command, in result it does not find enclosing }, cause it's been eaten by a command. You need a newline:
sed -i "/${1}/{N;N;a ${2}
}" "${3}"
or
sed -i "/${1}/{N;N;a ${2}"$'\n'"}" "${3}"

this should work:
sed -i '/search/{G;ainsert
}' file
You can replace the text by shell variable, but replace the single quotes by double quotes too.

Related

Not able to add a line of text after pattern using sed in OSX

I'm trying to add a line in a file afile.xyz using my script. This is what I've done so far using sed:
n="$(grep ".method" "$m" | grep "onCreate(Landroid/os/Bundle;)V")"
sed -i '' -e '/$n/ a\
"test", /Users/username/Documents/afile.xyz
I'm getting the error:
"onCreate\(\Landroid\/ ...": bad flag in substitute command: 'g'
How do I solve this? Please do help. Thanks.
Edit: Content of n
method protected onCreate(Landroid/os/Bundle;)V
2 problems:
because the sed body is in single quotes, the variable $n will not be expanded,
the regular expression in $n contains the / dilimiters.
Try this:
n=$(...)
nn=${n//\//\\/} # escape all slashes
sed -i '' '/'"${nn}"'/ a ...
The single-quoted sed body is interrupted to append the double quoted shell variable.
You can also use a different delimiter for the RE:
sed -i '' -e "\#$n# a\\
\"test\"" /Users/username/Documents/afile.xyz

Substitute string with file content in sed

I'm lost trying to do following substitution with sed:
# edit: to capture the full complexity of my problem,
I added the fact that filenames are contained in variables afterwards.
Solutions might therefore directly use the filenames.
given a variable I='insert.txt':
'insert.txt':
Text I wanna skip.
This is text to insert containing
spaces and new lines
given a variable M='toModify.txt':
'toModify.txt':
Insert the new text: here.
I would like to replace the 'here' from $M with the content
of $I:
Insert the new text: This is text to insert containing
spaces and new lines.
I tried:
sed -e "s/here/$(tail -n2 $I | sed -e 's/ /\\ /g' | tr '\n' '\\n')/" $M
with error:
sed unterminated `s' command
The problem is that I don't get the spaces and new lines without terminating the s command.
Any solution?
You can't replace one character with two with tr. Escaping the individual spaces is pointless anyway. The reason for the immediate error is that you end up escaping the final slash, too:
linux$ tail -n2 "$I" | sed -e 's/ /\\ /g' | tr '\n' '\\n'
This\ is\ text\ to\ insert\ containing\spaces\ and\ new\ lines\/
Escaping the spaces is pointless anyway. I guess you want something like this:
linux$ sed '1,2d;$!s/$/\\/' "$I"
This is text to insert containing\
spaces and new lines
We delete lines 1 and two; then add a backslash before every newline except the last.
linux$ sed -e "s/here/$(sed '1,2d;$!s/$/\\/' "$I")/" "$M"
Insert the new text: This is text to insert containing
spaces and new lines.
This is one detail of sed which isn't entirely portable. But the above works for me on Linux and MacOS. (Note you might need to set +H to disable csh-style history expansion aka -bash: !s/$/\\/': event not found errors).
You may use this awk:
awk 'BEGIN{prs=RS; RS=""; getline s < "insert.txt"; RS=prs}
{gsub(/here/, s)} 1' toModify.txt
Insert the new text: This is text to insert containing
spaces and new lines.
Using Perl one-liner
> cat insert.txt
This is text to insert containing
spaces and new lines
> cat toModify.txt
Insert the new text: here
> export I=insert.txt
> export M=toModify.txt
> perl -ne 'BEGIN{$x=qx(cat $ENV{M});$x=~s/here/qx(cat $ENV{I})/e; print $x;exit }'
Insert the new text: This is text to insert containing
spaces and new lines
>

How do I replace text using a variable in a shell script

I have a variable with a bunch of data.
text = "ABCDEFGHIJK"
file = garbage.txt //iiuhdsfiuhdsihf]sdiuhdfoidsoijsf
What I would like to do is replace the ] charachter in file with text. I've tried using sed but I keep getting odd errors.
output should be:
//iiuhdsfiuhdsihfABCDEFGHIJKsdiuhdfoidsoijsf
Just need to escape the ] character with a \ in regex:
text="ABCDEFGHIJK"
sed "s/\(.*\)\]\(.*\)/\1$text\2/" file > file.changed
or, for in-place editing:
sed -i "s/\(.*\)\]\(.*\)/\1$text\2/" file
Test:
sed "s/\(.*\)\]\(.*\)/\1$text\2/" <<< "iiuhdsfiuhdsihf]sdiuhdfoidsoijsf"
# output => iiuhdsfiuhdsihfABCDEFGHIJKsdiuhdfoidsoijsf
There is always the bash way that should work in your osx:
filevar=$(cat file)
echo "${filevar/]/$text}" #to replace first occurence
OR
echo "${filevar//]/$text}" #to replace all occurences
In my bash i don't even have to escape ].
By the way, the simple sed does not work?
$ a="AA"
$ echo "garbage.txt //iiuhdsfiuhdsihf]sdiuhdfoidsoijsf" |sed "s/]/$a/g"
garbage.txt //iiuhdsfiuhdsihfAAsdiuhdfoidsoijsf

String manipulation via script

I am trying to get a substring between &DEST= and the next & or a line break.
For example :
MYREQUESTISTO8764GETTHIS&DEST=SFO&ORIG=6546
In this I need to extract "SFO"
MYREQUESTISTO8764GETTHIS&DEST=SANFRANSISCO&ORIG=6546
In this I need to extract "SANFRANSISCO"
MYREQUESTISTO8764GETTHISWITH&DEST=SANJOSE
In this I need to extract "SANJOSE"
I am reading a file line by line, and I need to update the text after &DEST= and put it back in the file. The modification of the text is to mask the dest value with X character.
So, SFO should be replaced with XXX.
SANJOSE should be replaced with XXXXXXX.
Output :
MYREQUESTISTO8764GETTHIS&DEST=XXX&ORIG=6546
MYREQUESTISTO8764GETTHIS&DEST=XXXXXXXXXXXX&ORIG=6546
MYREQUESTISTO8764GETTHISWITH&DEST=XXXXXXX
Please let me know how to achieve this in script (Preferably shell or bash script).
Thanks.
$ cat file
MYREQUESTISTO8764GETTHIS&DEST=SFO&ORIG=6546
MYREQUESTISTO8764GETTHIS&DEST=PORTORICA
MYREQUESTISTO8764GETTHIS&DEST=SANFRANSISCO&ORIG=6546
MYREQUESTISTO8764GETTHISWITH&DEST=SANJOSE
$ sed -E 's/^.*&DEST=([^&]*)[&]*.*$/\1/' file
SFO
PORTORICA
SANFRANSISCO
SANJOSE
should do it
Replacing airports with an equal number of Xs
Let's consider this test file:
$ cat file
MYREQUESTISTO8764GETTHIS&DEST=SFO&ORIG=6546
MYREQUESTISTO8764GETTHIS&DEST=SANFRANSISCO&ORIG=6546
MYREQUESTISTO8764GETTHISWITH&DEST=SANJOSE
To replace the strings after &DEST= with an equal length of X and using GNU sed:
$ sed -E ':a; s/(&DEST=X*)[^X&]/\1X/; ta' file
MYREQUESTISTO8764GETTHIS&DEST=XXX&ORIG=6546
MYREQUESTISTO8764GETTHIS&DEST=XXXXXXXXXXXX&ORIG=6546
MYREQUESTISTO8764GETTHISWITH&DEST=XXXXXXX
To replace the file in-place:
sed -i -E ':a; s/(&DEST=X*)[^X&]/\1X/; ta' file
The above was tested with GNU sed. For BSD (OSX) sed, try:
sed -Ee :a -e 's/(&DEST=X*)[^X&]/\1X/' -e ta file
Or, to change in-place with BSD(OSX) sed, try:
sed -i '' -Ee :a -e 's/(&DEST=X*)[^X&]/\1X/' -e ta file
If there is some reason why it is important to use the shell to read the file line-by-line:
while IFS= read -r line
do
echo "$line" | sed -Ee :a -e 's/(&DEST=X*)[^X&]/\1X/' -e ta
done <file
How it works
Let's consider this code:
search_str="&DEST="
newfile=chart.txt
sed -E ':a; s/('"$search_str"'X*)[^X&]/\1X/; ta' "$newfile"
-E
This tells sed to use Extended Regular Expressions (ERE). This has the advantage of requiring fewer backslashes to escape things.
:a
This creates a label a.
s/('"$search_str"'X*)[^X&]/\1X/
This looks for $search_str followed by any number of X followed by any character that is not X or &. Because of the parens, everything except that last character is saved into group 1. This string is replaced by group 1, denoted \1 and an X.
ta
In sed, t is a test command. If the substitution was made (meaning that some character needed to be replaced by X), then the test evaluates to true and, in that case, ta tells sed to jump to label a.
This test-and-jump causes the substitution to be repeated as many times as necessary.
Replacing multiple tags with one sed command
$ name='DEST|ORIG'; sed -E ':a; s/(&('"$name"')=X*)[^X&]/\1X/; ta' file
MYREQUESTISTO8764GETTHIS&DEST=XXX&ORIG=XXXX
MYREQUESTISTO8764GETTHIS&DEST=XXXXXXXXXXXX&ORIG=XXXX
MYREQUESTISTO8764GETTHISWITH&DEST=XXXXXXX
Answer for original question
Using shell
$ s='MYREQUESTISTO8764GETTHIS&DEST=SFO&ORIG=6546'
$ s=${s#*&DEST=}
$ echo ${s%%&*}
SFO
How it works:
${s#*&DEST=} is prefix removal. This removes all text up to and including the first occurrence of &DEST=.
${s%%&*} is suffix removal_. It removes all text from the first & to the end of the string.
Using awk
$ echo 'MYREQUESTISTO8764GETTHIS&DEST=SFO&ORIG=6546' | awk -F'[=\n]' '$1=="DEST"{print $2}' RS='&'
SFO
How it works:
-F'[=\n]'
This tells awk to treat either an equal sign or a newline as the field separator
$1=="DEST"{print $2}
If the first field is DEST, then print the second field.
RS='&'
This sets the record separator to &.
With GNU bash:
while IFS= read -r line; do
[[ $line =~ (.*&DEST=)(.*)((&.*|$)) ]] && echo "${BASH_REMATCH[1]}fooooo${BASH_REMATCH[3]}"
done < file
Output:
MYREQUESTISTO8764GETTHIS&DEST=fooooo&ORIG=6546
MYREQUESTISTO8764GETTHIS&DEST=fooooo&ORIG=6546
MYREQUESTISTO8764GETTHISWITH&DEST=fooooo
Replace the characters between &DEST and & (or EOL) with x's:
awk -F'&DEST=' '{
printf("%s&DEST=", $1);
xlen=index($2,"&");
if ( xlen == 0) xlen=length($2)+1;
for (i=0;i<xlen;i++) printf("%s", "X");
endstr=substr($2,xlen);
printf("%s\n", endstr);
}' file

sed with replacement part given as input from command line

I am trying to pass command line argument as the replacement part to sed command. But $1 doesn't work for me.
For example I unsuccesfully tried
function changeversion() {
sed -i 's/[0-9]\+/$1/g' file.xml;
}
which only replaced all numbers in file with '$1' text.
How can I fix it?
I think you need to put the expression in double qoutes to get variable substitution
function changeversion() {
sed -i "s/[0-9]\+/$1/g" file.xml;
}

Resources