Can't get bash regular expression work - regex

This is part of my script:
if [[ `hostname --fqdn` != '(\S+-laptop)' ]]; then
echo "Wrong node, run it on server"
exit 1
fi
echo "testing ok"
exit 0
this is result:
++ hostname --fqdn
+ [[ mylinux1-laptop != \(\\\S\+\-\l\a\p\t\o\p\) ]]
+ echo 'Wrong node, run it on server'
Wrong node, run it on server
+ exit 1
I tested it on online tools and worked - can't figure why not in shell...
Thanks for help.

Correct BASH regex syntax is:
[[ ! "$(hostname --fqdn)" =~ [^[:space:]]+-laptop ]] && echo "Wrong node!" && exit 1
regex in BASH don't require quotes around them
\S doesn't work on BASH regex engine
Use [^[:space:]] to match anything but whitespace
BASH regex operator is =~
Negation should be at the start of the condition
You can also use shell glob instead of regex:
[[ "$(hostname --fqdn)" != [^\ ]*"-laptop" ]]

Related

Issue with RegEx in bash?

I have the following RegEx written to match any no of repeating patterns. It is working in https://regex101.com/ when tested online. But, it is not working when used in Linux Bash. Please help!!
pair_format="^([[:blank:]]*\[[[:blank:]]*[^=[\]]+[[:blank:]]*=[[:blank:]]*[^=[\]]+[[:blank:]]*\][[:blank:]]*)+$"
Sample data to test:
CUSTOM_ARGS_KV="[X=Y][A=B][C=D][FASLFJSDLF=9]"
if [[ ! $CUSTOM_ARGS_KV =~ $pair_format ]]; then; echo "invalid!!!!"; else echo "valid"; fi
Here is my script:
CUSTOM_ARGS_KV='[X=Y][A=B][C=D][FASLFJSDLF=9]' #example input
if [ ! -z "$CUSTOM_ARGS_KV" ]; then
pair_format="^([[:blank:]]*\[[[:blank:]]*[^=[\]]+[[:blank:]]*=[[:blank:]]*[^=[\]]+[[:blank:]]*\][[:blank:]]*)+$"
if [[ ! $CUSTOM_ARGS_KV =~ $pair_format ]]; then
echo "Error! CUSTOM_ARGS_KV is not according to format [key1=value1] [key2=value2] etc. Or either of key/value of a pair are kept blank"
exit 1
fi
fi
Not quoting the test works for me. Example :
pair_format="^([[:blank:]]*\[[[:blank:]]*[^=[\]]+[[:blank:]]*=[[:blank:]]*[^=[\]]+[[:blank:]]*\][[:blank:]]*)+$"
[[ ! "[X=Y][A=B][C=D][FASLFJSDLF=9]" =~ $pair_format ]] && echo "Match"
Output
Match
Regards!
Edit
Correcting your script here. This worked perfectly here :
CUSTOM_ARGS_KV='[X=Y][A=B][C=D][FASLFJSDLF=9]'
if [ ! -z "$CUSTOM_ARGS_KV" ];
then
pair_format='^([[:blank:]]*\[[[:blank:]]*[^=[\]]+[[:blank:]]*=[[:blank:]]*[^=[\]]+[[:blank:]]*\][[:blank:]]*)+$'
if [[ ! $CUSTOM_ARGS_KV =~ $pair_format ]]
then
echo 'Error! CUSTOM_ARGS_KV is not according to format [key1=value1] [key2=value2] etc. Or either of key/value of a pair are kept blank'
#exit 1
fi
fi
This regex should work for you:
pair_format="^(\[[^]=[]+=[^]=[]\])+$"
CUSTOM_ARGS_KV="[X=Y][A=B][C=D][FASLFJSDLF=9]"
[[ $CUSTOM_ARGS_KV =~ $pair_format ]] && echo "valid" || echo "invalid"
valid
It is very important to keep ] at first position in bracket expression after ^ and keep [ at last position.
PS: I have removed [[:blank:]]* fromn regex for sake of readability.
Code Demo
I ran your code and get the following error:
./testing: line 3: =[X=Y][A=B][C=D][FASLFJSDLF=9]: command not found
./testing: line 5: syntax error near unexpected token `;'
./testing: line 5: `if [[ ! "$CUSTOM_ARGS_KV" =~ "$pair_format" ]]; then; echo "invalid!!!!"; else echo "valid"; fi'
The line 3 problem was that I didn't take of the $ from CUSTOM_ARGS_KV.
Use the if like this:
if [[ ! "$CUSTOM_ARGS_KV" =~ "$pair_format" ]]
then
....echo "invalid!!!!"
else
....echo "valid"
fi

Using regular expressions in a ksh Script

I have a file (file.txt) that contains some text like:
000000000+000+0+00
000000001+000+0+00
000000002+000+0+00
and I am trying to check each line to make sure that it follows the format:
character*9, "+", character*3, "+", etc
so far I have:
#!/bin/ksh
file=file.txt
line_number=1
for line in $(cat $file)
do
if [[ "$line" != "[[.]]{9}+[[.]]{3}+[[.]]{1}+[[.]]{2} ]" ]]
then
echo "Invalid number ($line) check line $line_number"
exit 1
fi
let "line_number++"
done
however this does not evaluate correctly, no matter what I put in the lines the program terminates.
When you want line numbers of the mismatches, you can use grep -vn. Be careful with writing a correct regular expression, and you will have
grep -Evn "^.{9}[+].{3}[+].[+].{2}$" file.txt
This is not in the layout that you want, so change the layout with sed:
grep -Evn "^.{9}[+].{3}[+].[+].{2}$" file.txt |
sed -r 's/([^:]*):(.*)/Invalid number (\2) check line number \1./'
EDIT:
I changed .{1} into ..
The sed is also over the top. When you need spme explanation, you can start with echo "Linenr:Invalid line"
I'm having funny results putting the regex in the condition directly:
$ line='000000000+000+0+00'
$ [[ $line =~ ^.{9}\+.{3}\+.\+..$ ]] && echo ok
ksh: syntax error: `~(E)^.{9}\+.{3}\+.\+..$ ]] && echo ok
' unexpected
But if I save the regex in a variable:
$ re="^.{9}\+.{3}\+.\+..$"
$ [[ $line =~ $re ]] && echo ok
ok
So you can do
#!/bin/ksh
file=file.txt
line_number=1
re="^.{9}\+.{3}\+.\+..$"
while IFS= read -r line; do
if [[ ! $line =~ $re ]]; then
echo "Invalid number ($line) check line $line_number"
exit 1
fi
let "line_number++"
done < "$file"
You can also use a plain glob pattern:
if [[ $line != ?????????+???+?+?? ]]; then echo error; fi
ksh glob patterns have some regex-like syntax. If there's an optional space in there, you can handle that with the ?(sub-pattern) syntax
pattern="?????????+???+?( )?+??"
line1="000000000+000+0+00"
line2="000000000+000+ 0+00"
[[ $line1 == $pattern ]] && echo match || echo no match # => match
[[ $line2 == $pattern ]] && echo match || echo no match # => match
Read the "File Name Generation" section of the ksh man page.
Your regex looks bad - using sites like https://regex101.com/ is very helpful. From your description, I suspect it should look more like one of these;
^.{9}\+.{3}\+.{1}\+.{2}$
^[^\+]{9}\+[^\+]{3}\+[^\+]{1}\+[^\+]{2}$
^[0-9]{9}\+[0-9]{3}\+[0-9]{1}\+[0-9]{2}$
From the ksh manpage section on [[ - you would probably want to be using =~.
string =~ ere
True if string matches the pattern ~(E)ere where ere is an extended regular expression.
Note: As far as I know, ksh regex doesn't follow the normal syntax
You may have better luck with using grep:
# X="000000000+000+0+00"
# grep -qE "^[^\+]{9}\+[^\+]{3}\+[^\+]{1}\+[^\+]{2}$" <<<"${X}" && echo true
true
Or:
if grep -qE "^[^\+]{9}\+[^\+]{3}\+[^\+]{1}\+[^\+]{2}$" <<<"${line}"
then
exit 1
fi
You may also prefer to use a construct like below for handling files:
while read line; do
echo "${line}";
done < "${file}"

The equal tilde operator is not working in bash 4

In a server with bash version 3 I do this:
bash3$ e="tar xfz"; [[ "$e" =~ "^tar" ]] && echo 0 || echo 1
0
But when I execute the same command in bash version 4
bash4$ e="tar xfz"; [[ "$e" =~ "^tar" ]] && echo 0 || echo 1
1
I tried it in CentOS, Fedora and Ubuntu and got the same results. What is wrong?
Quoting the section on regular expressions from Greg's Wiki:
Before 3.2 it was safe to wrap your regex pattern in quotes but this has changed in 3.2. Since then, regex should always be unquoted.
This is the most compatible way of using =~:
e='tar xfz'
re='^tar'
[[ $e =~ $re ]] && echo 0 || echo 1
This should work on both versions of bash.
In this case, where you just want to make sure that your parameter starts with tar, you don't need regular expression matching, simple pattern matching works as well:
e='tar xfz'
[[ $e == tar* ]] && echo 0 || echo 1

match leading dots in bash if using regex

Say I want to match the leading dot in a string ".a"
So I type
[[ ".a" =~ ^\. ]] && echo "ha"
ha
[[ "a" =~ ^\. ]] && echo "ha"
ha
Why am I getting the same result here?
You need to escape the dot it has meaning beyond just a period - it is a metacharacter in regex.
[[ "a" =~ ^\. ]] && echo "ha"
Make the change in the other example as well.
Check your bash version - you need 4.0 or higher I believe.
There's some compatibility issues with =~ between Bash versions after 3.0. The safest way to use =~ in Bash is to put the RE pattern in a var:
$ pat='^\.foo'
$ [[ .foo =~ $pat ]] && echo yes || echo no
yes
$ [[ foo =~ $pat ]] && echo yes || echo no
no
$
For more details, see E14 on the Bash FAQ page.
Probably it's because bash tries to treat "." as a \ character, like \n \r etc.
In order to tell \ & . as 2 separate characters, try
[[ "a" =~ ^\\. ]] && echo ha

Regex matches from command line, doesn't match from bash script

I'm a bit confused: how come a regular expression works perfectly well using grep from command line and as I use the exactly same regular expression in a bash conditional statement, it doesn't work at all?
I'd like to match all the strings containing letters only, therefore my regular expression is:
^[a-zA-Z]\+$.
Please will you help sort this out?
Here's the snippet from my bash code
if ! [[ "$1" =~ '^[a-zA-z]+$' ]] ; then
echo "Error: illegal input string." >&2
exit 1
fi
Don't escape the +.
This works for me:
$ [[ "Abc" =~ ^[a-zA-Z]+$ ]] && echo "it matches"
$ it matches
Also, you don't need single quotes around the regex. The following works for me:
if ! [[ "$1" =~ ^[a-zA-z]+$ ]] ; then
echo "Error: illegal input string." >&2
exit 1
fi