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
Related
i want to match my 2 strings index and index1 in if condition of shell programming
i tried doing this by following
if [[ $1 == [iI][nN][dD][eE][xX][1]? ]]; then
echo "matched"
but it is not working, here basically i want to say in my regular expression that 1 should occur either 0 or 1 time.
Thanks in advance!
You need to use =~ operator to match regex and make sure to use anchors ^ and $ to avoid matching unwanted text:
[[ 'index1' =~ ^[iI][nN][dD][eE][xX]1?$ ]] && echo "ok" || echo "nope"
ok
[[ 'index' =~ ^[iI][nN][dD][eE][xX]1?$ ]] && echo "ok" || echo "nope"
ok
I'm reading a file in bash, line by line. I need to print lines that have the following format:
don't care <<< at least one character >>> don't care.
These are all the way which I have tried and none of them work:
if [[ $line =~ .*<<<.+>>>.* ]]; then
echo "$line"
fi
This has incorrect syntax
These two have correct syntax don't work
if [[ $line =~ '.*<<<.+>>>.*' ]]; then
echo "$line"
fi
And this:
if [[ $line == '*<<<*>>>*' ]]; then
echo "$line"
fi
So how to I tell bash to only print lines with that format? PD: I have tested and printing all lines works just fine.
Don't need regular expression. filename patterns will work just fine:
if [[ $line == *"<<<"?*">>>"* ]]; then ...
* - match zero or more characters
? - match exactly one character
"<<<" and ">>>" - literal strings: The angle brackets need to be quoted so bash does not interpret them as a here-string redirection.
$ line=foobar
$ [[ $line == *"<<<"?*">>>"* ]] && echo y || echo n
n
$ line='foo<<<>>>bar'
$ [[ $line == *"<<<"?*">>>"* ]] && echo y || echo n
n
$ line='foo<<<x>>>bar'
$ [[ $line == *"<<<"?*">>>"* ]] && echo y || echo n
y
$ line='foo<<<xyz>>>bar'
$ [[ $line == *"<<<"?*">>>"* ]] && echo y || echo n
y
For maximum compatibility, it's always a good idea to define your regex pattern as a separate variable in single quotes, then use it unquoted. This works for me:
re='<<<.+>>>'
if [[ $line =~ $re ]]; then
echo "$line"
fi
I got rid of the redundant leading/trailing .*, by the way.
Of course, I'm assuming that you have a valid reason to process the file in native bash (if not, just use grep -E '<<<.+>>>' file)
<, <<, <<<, >, and >> are special in the shell and need quoting:
[[ $line =~ '<<<'.+'>>>' ]]
. and + shouldn't be quoted, though, to keep their special meaning.
You don't need the leading and trailing .* in =~ matching, but you need them (or their equivalents) in patterns:
[[ $line == *'<<<'?*'>>>'* ]]
It's faster to use grep to extract lines:
grep -E '<<<.+>>>' input-file
I don't even understand why you are reading the file line per line. I have just launched following command in the bash prompt and it's working fine:
grep "<<<<.+>>>>" test.txt
where test.txt contains following data:
<<<<>>>>
<<<<a>>>>
<<<<aa>>>>
The result of the command was:
<<<<a>>>>
<<<<aa>>>>
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" ]]
I don't mean that this question for UNIX only, but I work on Solaris, and I didn't try it on any other OS.
I confused between the extended regular expression:
first:
[[ "str" == ?(str|STR) ]] && echo "matched"
this work correct, but when:
[[ "str str" == ?(str|STR)(.*) ]] && echo "matched"
it doesn't work, does it mean that I can only compare one pattern.
Second:
[[ "str" =~ ?(str|STR) ]] && echo "matched"
I can't use this form here why?, but when:
[[ "str" == (str|STR)? ]] && echo "matched"
it works correctly.
It looks like you are trying to combine
extended globs
with
extended regular expressions. I would say this is A Bad Thing.
$ set '(str|STR)'
$ [[ 'str' =~ $1 ]] && echo matches
matches
$ [[ 'str str' =~ $1 ]] && echo matches
matches
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