Can't match regex in string equality - bash

I just can't match the $move with regex, move is a chess move f.e. "a2 a4". It always goes to else part... thanks for any help
read move
if [[ "$move" =~ "[a-h][1-8] [a-h][1-8]" ]] ; then
CheckMove "$move" 1
else
printf "Invalid move format \n"
fi

Don't quote your regex pattern inside the if condition. That makes it act like a simple string equality test. Use an unquoted variable:
read move
pattern="[a-h][1-8] [a-h][1-8]"
if [[ $move =~ $pattern ]] ; then
Inside double square brackets you don't need to quote your $move variable either.
Also, don't use single square brackets in a Bash-specific script.
if [[ $move_valid = true ]] ; then
I don't see where that variable gets set, by the way.

Related

Regular expression in bash not working in conditional construct in Bash with operator '=~'

The regular expression I have put into the conditional construct (with the =~ operator) would not return the value as I had expected, but when I assign them into two variables it worked. Wondering if I had done something wrong.
Version 1 (this one worked)
a=30
b='^[0-9]+$' #pattern looking for a number
[[ $a =~ $b ]]
echo $?
#result is 0, as expected
Version 2 (this one doesn't work but I thought it is identical)
[[ 30 =~ '^[0-9]+$' ]]
echo $?
#result is 1
Don't quote the regular expression:
[[ 30 =~ ^[0-9]+$ ]]
echo $?
From the manual:
Any part of the pattern may be quoted to force the quoted portion to be matched as a string.
So if you quote the entire pattern, it's treated as a fixed string match rather than a regular expression.

need to remove the last zeros in line

need to read a file. check for the zeros in the last of each line . if the last digit is zero I want to delete it .please help me for this
input="temp.txt"
while IFS= read -r line
do
echo "output :$line"
if [[ $line == 0$ ]]; then
echo " blash "
else
echo "anotherblash"
fi
done < "$input"
You can do this type of substitution with sed:
sed 's/0*$//' temp.txt
This removes all the trailing zeros from each line. 0* matches "zero or more" 0s, and $ matches the end of the line.
If you only ever want to remove one 0, then remove the *.
If you prefer to do the same thing in the shell (I assume you use bash, since your attempt includes [[), you could do this:
#!/bin/bash
# match any line ending in one or more zeros
# capture everything up to the trailing 0s
re='(.*[^0])0+$'
while read -r line; do
# use =~ for regex match
if [[ $line =~ $re ]]; then
# assign first capture group, discarding trailing 0s
line=${BASH_REMATCH[1]}
fi
echo "$line"
done < temp.txt
But this approach has the disadvantages of being more complicated and less portable, so I would go with the sed option.
In the expression command [[ $line == 0$ ]] you use the regular expression 0$, but, as man sh tells:
When the == and != operators are used, the string to the right
of the operator is considered a pattern and matched according to
the rules described below under Pattern Matching. …
An additional binary operator, =~, is available, with the same
precedence as == and !=. When it is used, the string to the
right of the operator is considered an extended regular expres-
sion and matched accordingly (as in regex(3)).
So, since you use the == operator, you have to specify a pattern as with filename matching, i. e. [[ $line == *0 ]].
While the solution given by John1024 in the comment is the right way to go, if you prefer to follow your original approach, it does not make sense to compare [[ $line == 0$ ]], because this would just check whether the line consists of the digit zero, forllowed by a dollar sign. Instead, you would have to do a regular expression match, i.e.
if [[ $line =~ 0$ ]]
This would yield true, if the line ends in a zero.
Another possibility is to stick with globbing and write the condition as
if [[ $line == *0 ]]
Note that within [[ ... ]], a =~ does regexp matching and a == does wildcard matching (i.e. via globbing).

Script Shell : Case issue

I have a script when you select a desktop file, but when I run this case function:
File=$(yad --file);
if [[ "$File" =~ *".desktop" ]]; then
echo "yes"
else
echo "no"
if
and i try this :
File=$(yad --file);
case $File in
*.desktop )
echo "yes"
;;
* )
echo "no"
;;
esac
it's always telling me that I have to try again I don't know what's the problem, can anyone help me?
I am not exactly sure what this script is supposed to do, but try this:
File="$(Yad --file)"
if [[ "$File" =~ .*[.]desktop$ ]]; then
echo "yes"
else
echo "no"
fi
Bash regular expression matching (=~) uses extended regular expressions, not glob expressions. To designate any sequence of zero or more characters, you need to use .*. The . means "any character", and the * means zero or more times. [.] designates a literal period, avoiding the "any character" meaning of . used alone. I also added an end-of-line anchor ($). This forces the pattern to match from the end of the filename, as you probably would want when matching with the extension.
There also is an error in your first line. There has to be no space between the $ sign and parentheses. And to close an if block, you need to use fi.
You can use glob-style matching with bash conditionals, just use an equal sign :
if [[ "$File" = *.desktop ]]; then

Search in string for multiple array values

I'm looking at a simple for loop with the following logic:
variable=`some piped string`
array_value=(1.1 2.9)
for i in ${array_value[#]}; do
if [[ "$variable" == *some_text*"$array_value" ]]; then
echo -e "Info: Found a matching string"
fi
The problem is that I cannot get this to show me when it finds either the string ending in 1.1 or 2.9 as sample data.
If I do an echo $array_value in the for loop I can see that the array values are being taken so its values are being parsed, though the if loop doesn't return that echo message although the string is present.
LE:
Based on the comments received I've abstracted the code to something like this, which still doesn't work if I want to use wildcards inside the comparison quote
versions=(1.1 2.9)
string="system is running version:2.9"
for i in ${versions[#]}; do
if [[ "$string" == "system*${i}" ]]; then
echo "match found"
fi
done
Any construction similar to "system* ${i}" or "* ${i}" will not work, though if I specify the full string pattern it will work.
The problem with the test construct has to you with your if statement. To construct the if statement in a form that will evaluate, use:
if [[ "$variable" == "*some_text*${i}" ]]; then
Note: *some_text* will need to be replaced with actual text without * wildcards. If the * is needed in the text, then you will need to turn globbing off to prevent expansion by the shell. If expansion is your goal, then protect the variable i by braces.
There is nothing wrong with putting *some_text* up against the variable i, but it is cleaner, depending on the length of some_text, to assign it to a variable itself. The easiest way to accommodate this would be to define a variable to hold the some_text you are needing. E.g.:
prefix="some_text"
if [[ "$variable" == "${prefix}${i}" ]]; then
If you have additional questions, just ask.
Change "system*${i}" to system*$i.
Wrapping with quotes inside [[ ... ]] nullifies the wildcard * by treating it as a literal character.
Or if you want the match to be assigned to a variable:
match="system*"
you can then do:
[[ $string == $match$i ]]
You actually don't need quotes around $string either as word splitting is not performed inside [[ ... ]].
From man bash:
[[ expression ]]
...
Word splitting and pathname expansion are not
performed on the words between the [[ and ]]
...
Any part of the pattern may be quoted to force
the quoted portion to be matched as a string.

BASH: Everything but not slash? IF STATEMENT (STRING COMPARISION)

I'm trying to match any strings that start with /John/ but does not contain / after /John/
if
[ $string == /John/[!/]+ ]; then ....
fi
This is what I got and it doesn't seem to be working.
So I tried
if
[[ $string =~ ^/John/[!/]+$ ]]; then ....
fi
It still didn't work, and so I changed it to
if
[[ $string =~ /John/[^/] ]]; then ....
fi
It worked but will match with all the strings that has / behind /John/ too.
For bash you want [[ $string =~ /John/[^/]*$ ]] -- the end-of-line anchor ensures there are no slashes after the last acceptable slash.
How about "the string starts with '/John/' and doesn't contain any slashes after '/John/'"?
[[ $string = /John/* && $string != /John/*/* ]]
Or you could compare against a parameter expansion that only expands if the conditions are met. This says "after stripping off everything including and after the last slash, the string is /John":
[[ ${string%/*} = /John ]]
In fact, this last solution is the only entirely POSIXLY_STRICT one I can come up with without multiple test expressions.
[ "${string%/*}" = /John ]
By the way, your problem is probably simply be using double-equals inside a single-bracket test expression. bash actually does accept them inside double-bracket test expressions, but a single equals is a better idea.
You can also use plain old grep:
string='/John Lennon/Yoko Ono'
if echo "$string" | grep -q "/John[^/]" ; then
echo "matched"
else
echo "no match found"
fi
This only fails if /John is at the very end of the string... if that's a possibility then you can tweak to handle that case, for instance:
string='/John Lennon/Yoko Ono'
if echo "$string" | grep -qP "(/John[^/])|(/John$)" ; then
echo "matched"
else
echo "no match found"
fi
Not sure what language you're using, but normal negative character classes are prefixed with a ^
e.g.
[^/]
You can also put in start/end qualifiers (clojure example, so Java's regex engine). Usually ^ at beginning and $ at end.
user => (re-matches #"^/[a-zA-Z]+[^/]$" "/John/")
nil

Resources