Bash String Comparison With Regex Not Working - sed

I'm trying to do a simple string comparison in zsh where one the arguments is the result of a sed call.
Yet I keep getting "zsh: missing end of string"
[ (sed -En s/'(^.+)-SNAPSHOT'/'\1'/p ./app_version.txt) = "1.20.11" ]
The app_version.txt file contains a Maven version. sed is stripping out "SNAPSHOT".
app_version.txt
1.20.11-SNAPSHOT

It's because the output of your sed needs to be double quoted. Try this:
[ "$(sed -En s/'(^.+)-SNAPSHOT'/'\1'/p ./app_version.txt)" = "1.20.11" ]

In your case, you don't even need regular expressions. You could do it with
if [[ $(<app_version.txt) == 1.20.11-SNAPSHOT ]]
or, if you want to make the SNAPSHOT part optional, something like
if [[ $(<app_version.txt) == 1.20.11-* ]]

Related

Capturing groups with sed command

I have strings like below
_c_VehCfg1_oCAN00_f276589c_In_Int_buf *pVehCfg1_oCAN00_f276589c_In_IntBuf = (_c_VehCfg1_oCAN00_f276589c_In_Int_buf *)can_Msg_tmp_buffer;
I want replace can_Msg_tmp_buffer with ptr as below
_c_VehCfg1_oCAN00_f276589c_In_Int_buf *pVehCfg1_oCAN00_f276589c_In_IntBuf = (_c_VehCfg1_oCAN00_f276589c_In_Int_buf *)ptr;
I have tried sed as below
echo "_c_VehCfg1_oCAN00_f276589c_In_Int_buf *pVehCfg1_oCAN00_f276589c_In_IntBuf = (_c_VehCfg1_oCAN00_f276589c_In_Int_buf *)can_Msg_tmp_buffer;" | sed 's/\(_C_[[:alnum:]_]*IntBuf = [[:alnum:]_]*\)can_Msg_tmp_buffer/1\ptr/g'
Still I'm not getting expected result instead sed output is same as input.
The problem is I have strings like below also
_c_GW_C4_oCAN00_f276589c_In_Moto_buf *pGW_C4_oCAN00_f276589c_In_MotoBuf = (_c_GW_C4_oCAN00_f276589c_In_Moto_buf *)can_Msg_tmp_buffer;
I only want to replace where type is ending with _Int_buf not _Moto_buf.
It gets extremely convoluted to match individual words with a regex and get a captured group out of it. One way would be to work with known parts of the string which are guaranteed to occur.
For your case, using the strings _In_IntBuf and can_Msg_tmp_buffer; we try to uniquely identify those pattern of lines and do the substitution
sed 's/\(.*\)_In_IntBuf = \(.*\)can_Msg_tmp_buffer;/\1_In_IntBuf = \2ptr;/'
In case you are ok with awk try following.
awk '/_In_IntBuf =/{sub(/can_Msg_tmp_buffer/,"ptr")} 1' Input_file
In case you want to save output into Input_file itself append > temp_file && mv temp_file Input_file in above code.

Need assistance with escapes in my groovy command

I need to replace a version string in a file. My search pattern is regex
and my replacement is a variable.
String search = "\\d+.\\d+.\\d+-.\\d+"
String replace = "1.0.0-${BUILD_ID}"
MyFile = "foo"
sh ("""
sed -i -r "s/($search/$replace/g)" $MyFile
""")
The result I am getting
+ sed -i -r s/(\d+.\d+.\d+-.\d+/1.0.0-25/g) foo
sed: bad option in substitution expression
I found the issue with my code. If I remove parenthesis (), the string replacement works as a charm.

sed substitute with quotes and wildcard

I need to replace if ($_SESSION['POST']['*']==1){ with if (isset($_SESSION['POST']['*']) && $_SESSION['POST']['*']==1){
(I'm using * as a wild card)
I've tried sed -i "s/if ($_SESSION['POST']['.*']/if (isset($_SESSION['POST']['.*']) && $_SESSION['POST']['.*']/g" *.php and a few other variations without success.
Here goes...
sed "s/\(if (\)\(\$_SESSION\['POST']\['\([^']*\)']\)==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
Using double quotes means that the $ symbols must be escaped, otherwise they will be interpreted as shell variables. The square brackets need to be escaped, otherwise they will be interpreted as the beginning of a range. It's OK to leave the closing square brackets as they are.
In order to capture the key, I have used a character class [^']*. This means zero or more characters that are not a single quote.
In the replacement, the captured groups (the parts between parentheses in the match) are referred to using \1, \2, etc.
Testing it out:
$ cat file
if ($_SESSION['POST']['foo']==1){
// do something
}
if ($_SESSION['POST']['bar']==1){
// do something else
}
$ sed "s/\(if (\)\(\$_SESSION\['POST']\['\([^']*\)']\)==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
if (isset($_SESSION['POST']['foo']) && $_SESSION['POST']['foo']==1){
// do something
}
if (isset($_SESSION['POST']['bar']) && $_SESSION['POST']['bar']==1){
// do something else
}
By the way it makes the command a few characters shorter if you use extended regexp mode (-r or -E). In extended mode, the parentheses enclosing capture groups don't have to be escaped but literal ones do, so your command would then be:
sed -r "s/(if \()(\$_SESSION\['POST']\['([^']*)'])==1/\1isset(\2) \&\& \$_SESSION['POST']['\3']==1/" file
This sed should work:
s="if (\$_SESSION['POST']['name']==1){"
sed -r 's/(if +)\((([^=]+)[^\)]+)/\1(isset(\3) \&\& \2/' <<< "$s"
if (isset($_SESSION['POST']['name']) && $_SESSION['POST']['name']==1){
PS: Use sed -E instead of sed -r on OSX.
Here's another.
This is what we need to produce:
Pattern: if (\$_SESSION\['POST'\]\['\([^']*\)'\]
Replacement: if (isset($_SESSION['POST']['\1']) \&\& $_SESSION['POST']['\1']
When quoted in shell level:
Pattern: "if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]"
Replacement: "if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']"
Putting it together:
sed -i "s|if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]|if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']|g" file
Test:
# sed "s|if (\\\$_SESSION\['POST'\]\['\([^']*\)'\]|if (isset(\$_SESSION['POST']['\1']) \\&\\& \$_SESSION['POST']['\1']|g" <<'EOF'
> if ($_SESSION['POST']['ABC']==1){
> EOF
if (isset($_SESSION['POST']['ABC']) && $_SESSION['POST']['ABC']==1){

how to write a not-so complex if-else statement in Bourne shell?

I'm trying to get a not-so complex if-else to work on my FreeBSD box but I'm getting error with the second part of the condition.
basically, this is what I'm trying to do
if not file-exists or (file-exists and string exists in file) then
do this
else
do something else
this is the actual code I'm using
if [ ! -f /boot/loader.conf ] || [[ -f /boot/loader.conf ] && ! grep -Fqx "zfs_l
oad" /boot/loader.conf ]; then
echo "found"
else
echo "not found"
fi
It gives me an error about "[[". I tried adding/removing brackets to no avail.
I've also searched the net for similar examples but the ones I've seen are very simplistic (i.e. if var=value then do this)
I could separate the conditions into 2 "ifs" but I think it can be done in 1 and I'm want to know "advance" if-else in bourne as well. :)
Any help would be greatly appreciated.
thanks :)
Use { ... } for grouping without the overhead and side-effects of a subshell (as created by ( ... )). [[ ]] is a different syntax, only available in ksh derivatives such as bash, which replaces [ ... ] with a less-error-prone alternative; it isn't available in baseline POSIX shells.
[ ! -f /boot/loader.conf ] || \
{ [ -f /boot/loader.conf ] && ! grep -Fqx "zfs_load" /boot/loader.conf; }

find the line number where a specific word appears with “sed” on tcl shell

I need to search for a specific word in a file starting from specific line and return the line numbers only for the matched lines.
Let's say I want to search a file called myfile for the word my_word and then store the returned line numbers.
By using shell script the command :
sed -n '10,$ { /$my_word /= }' $myfile
works fine but how to write that command on tcl shell?
% exec sed -n '10,$ { /$my_word/= }' $file
extra characters after close-brace.
I want to add that the following command works fine on tcl shell but it starts from the beginning of the file
% exec sed -n "/$my_word/=" $file
447431
447445
448434
448696
448711
448759
450979
451006
451119
451209
451245
452936
454408
I have solved the problem as follows
set lineno 10
if { ! [catch {exec sed -n "/$new_token/=" $file} lineFound] && [string length $lineFound] > 0 } {
set lineNumbers [split $lineFound "\n"]
foreach num $lineNumbers {
if {[expr {$num >= $lineno}] } {
lappend col $num
}
}
}
Still can't find a single line that solve the problem
Any suggestions ??
I don't understand a thing: is the text you are looking for stored inside the variable called my_word or is the literal value my_word?
In your line
% exec sed -n '10,$ { /$my_word/= }' $file
I'd say it's the first case. So you have before it something like
% set my_word wordtosearch
% set file filetosearchin
Your mistake is to use the single quote character ' to enclose the sed expression. That character is an enclosing operator in sh, but has no meaning in Tcl.
You use it in sh to group many words in a single argument that is passed to sed, so you have to do the same, but using Tcl syntax:
% set my_word wordtosearch
% set file filetosearchin
% exec sed -n "10,$ { /$my_word/= }" $file
Here, you use the "..." to group.
You don't escape the $ in $my_word because you want $my_word to be substitued with the string wordtosearch.
I hope this helps.
After a few trial-and-error I came up with:
set output [exec sed -n "10,\$ \{ /$myword/= \}" $myfile]
# Do something with the output
puts $output
The key is to escape characters that are special to TCL, such as the dollar sign, curly braces.
Update
Per Donal Fellows, we do not need to escape the dollar sign:
set output [exec sed -n "10,$ \{ /$myword/= \}" $myfile]
I have tried the new revision and found it works. Thank you, Donal.
Update 2
I finally gained access to a Windows 7 machine, installed Cygwin (which includes sed and tclsh). I tried out the above script and it works just fine. I don't know what your problem is. Interestingly, the same script failed on my Mac OS X system with the following error:
sed: 1: "10,$ { /ipsum/= }": extra characters at the end of = command
while executing
"exec sed -n "10,$ \{ /$myword/= \}" $myfile"
invoked from within
"set output [exec sed -n "10,$ \{ /$myword/= \}" $myfile]"
(file "sed.tcl" line 6)
I guess there is a difference between Linux and BSD systems.
Update 3
I have tried the same script under Linux/Tcl 8.4 and it works. That might mean Tcl 8.4 has nothing to do with it. Here is something else that might help: Tcl comes with a package called fileutil, which is part of the tcllib. The fileutil package contains a useful tool for this case: fileutil::grep. Here is a sample on how to use it in your case:
package require fileutil
proc grep_demo {myword myfile} {
foreach line [fileutil::grep $myword $myfile] {
# Each line is in the format:
# filename:linenumber:text
set lineNumber [lindex [split $line :] 1]
if {$lineNumber >= 10} { puts $lineNumber}
}
}
puts [grep_demo $myword $myfile]
Here is how to do it with awk
awk 'NR>10 && $0~f {print NR}' f="$my_word" "$myfile"
This search for all line larger than line number 10 that contains word in variable $my_word in file name stored in variable myfile