sed replace text with backslash \ and curved brackets {} using bash variable - bash

I have a line of latex source code which I want to replace.
The problem is, it contains curved brackets and backslash.
Furthermore, I would like to replace it with a bash variable
Before: In case0.tex, I have this line:
\title{Analysis Case 0}
I want to change the title inside the curved bracket to a string contained in a bash variable called $CASE.
This is what I tried, however I am not sure how to treat this special case with sed.
CASE=Analysis Case 1
sed -e "s/ \title{Analysis Case 0} / \title{ $CASE } /g" ./case0.tex > ./case1.tex
After: In case1.tex I would like to get this line.
\title{Analysis Case 1}
It would be nice if someone could tell me how to do that!

Using sed
$ case="Analysis Case 1"
$ sed s"/\({\)[^}]*/\1$case/" case0.tex > case1.tex
$ cat case1.tex
\title{Analysis Case 1}

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

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

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.

sed match only first expression

I'm doing a parser for build outputs, and I'd like to highlight different patterns in different colors. So for example, I'd like to do:
sed -e "s|\(Error=errcode1\)|<red>\1<_red>|" \
-e "s|\(Error=errcode2\)|<orange>\1<_orange>|" \
-e "s|\(Error=.*\)|<blue>\1<_blue>|"
(so it higlights errcode1 in red, errcode2 in orange, and anything else in blue). The problem with this is that Error=errcode1 matches both the first and the third expression, which will result in <red><blue>Error=errcode1<_red><_blue>... Is there any way to tell sed to match only the first expression, and if it does, do not try the following expressions?
Note, the sed command will actually be auto-generated from files which will be very volatile, so I'd like a generic solution where I don't have to police whether patterns conflict...
Let's start with a simpler example to illustrate the problem. In the code below, both substitutions are performed:
$ echo 'error' | sed 's/error/error2/; s/error/error3/'
error32
If we want to skip the second if the first succeeded, we can use the "test" command which branches if the previous substitution was successful. If we provide no label after t, it branches to the end, skipping all remaining commands:
$ echo 'error' | sed 's/error/error2/; t; s/error/error3/'
error2
Summary
If you want to stop after the first substitution that succeeds, place a t command after each substitution command.
More complex case
Suppose that we want to skip the second but not the third substitution if the first succeeds. In that case, we need to supply a label to the t command:
$ echo 'error' | sed 's/error/error2/; ta; s/error/error3/; :a; s/error/error4/'
error42
In the above, :a defines label a. The command ta branches to label a if the preceeding s command succeeds.
Compatibility
The above code was tested in GNU sed. I am told that BSD sed does not accept ; as a command separator after a label. Thus, on BSD/macOS, try:
echo 'error' | sed -e 's/error/error2/' -e ta -e 's/error/error3/' -e :a -e 's/error/error4/'
You can apply boolean logic to matches with |, & and !.
Solution
(not sure if the syntax is compatible with your system so you may need to add more backslashes)
"s|\(Error=\(.*&!errcode1&!errcode2\)\)|<blue>\1<_blue>|"
Other notes
sed can use any character as a delimiter, so all of the following expressions are equivalent:
"s/foo/bar/"
"s:foo:bar:"
"s|foo|bar|"
"s#foo#bar#"
Also, if you are using bash on a Unix-based system, you can use shell variables if you're running this from a script (since your patterns are enclosed with " and not ', there's a difference).
PREFIX="Error="
TARGET_1="errorcode1"
TARGET_2="errorcode2"
SUB_1="<red>\1<_red>"
SUB_2="<orange>\1<_orange>"
SUB_3="<blue>\1<_blue>"
sed -e "s|\($PREFIX$TARGET_1\)|$SUB_1|" \
-e "s|\($PREFIX$TARGET_2\)|$SUB_2|" \
-e "s|\($PREFIX\(.*&!$TARGET_1&!$TARGET_2\)\)|$SUB_3|" \
If the other errorcodes follow the naming scheme errcodeN, you can negate the 1,2:
sed -e "s|\(Error=errcode1\)|<red>\1<_red>|" \
-e "s|\(Error=errcode2\)|<orange>\1<_orange>|" \
-e "s|\(Error=errcode[^12]\)|<blue>\1<_blue>|"
If the codes exceed number 9: [^12]+
This is not a good application for sed, you should use awk instead. You didn't provide any sample input/output to test against so this is obviously untested but you'd do something like this:
awk '
BEGIN {
colors["errorcode1"] = "red"
colors["errorcode2"] = "orange"
colors["default"] = "blue"
}
match($0,/(.*Error=)([[:alnum:]]+)(.*)/,a) {
code = a[2]
color = (code in colors ? colors[code] : colors["default"])
$0 = sprintf("%s<%s>%s<_%s>%s", a[1], color, code, color, a[3])
}
{ print }
'
The above uses GNU awk for the 3rd arg to match(), it's a minor tweak for other awks.

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

Resources