Bash pattern matching inside [[ test block - regex

I am making a script that can do the same task on several machines, like this:
#!/bin/bash
if [[ hostname = login.machine1 ]]; then
#do things how they are done on machine1
...
elif [[ hostname = login.machine2 ]]; then
#etc.
fi
Problem: some machines have several login nodes, like login1.machine2, login2.machine2, etc. I have tried shell pattern matching and regex but none of them work:
if [[ hostname = login?.machine2 ]]
if [[ hostname == login?.machine2 ]]
if [[ hostname =~ login[0-9].machine2 ]]
if [[ hostname =~ login[0-9]\.machine2 ]]
and at least a dozen similar patterns, with or without quotes, etc. What is the correct way to make a test that will return true if hostname = login1.machine2 OR login2.machine2, etc.? Bonus points for something that works with [ instead of [[.

You need a $ before hostname to use the variable.
if [[ $hostname =~ login[0-9]\.machine2 ]]; then
echo "ok"
fi
And for the single [ (sh), the regexp operator =~ was added on bash, so it should not be possible to do it natively with sh.

Is [[ hostname =~ ... ]] supposed to call the command hostname or do you have a variable called hostname? Either use $HOSTNAME or the command substitution $(hostname)
If concerned about portability AWK can be used, with old school command substitutions:
if printf "%s\n" "`hostname`" | awk '!/login[[:digit:]]\.machine2/{exit 1}'; then
printf "Matches...\n"
else
printf "Doesn't match\n"
fi
If not I would highly recommend the answer by naab

The command hostname needs to be wrapped:
if [[ $(hostname) = login?.machine2 ]]

Related

Regex - validate IPv6 shell script

I am able to validate IPv6 addresses using java with following regex:
([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}
But I need to do this in shell script to which I am new.
This regex doesn't seem to work in shell. Have tried some other combinations also but nothing helped.
#!/bin/bash
regex="([0-9a-fA-F]{0,4}:){1,7}([0-9a-fA-F]){0,4}"
var="$1"
if [[ "$var" =~ "$regex" ]]
then
echo "matches"
else
echo "doesn't match!"
fi
It gives output doesn't match! for 2001:0Db8:85a3:0000:0000:8a2e:0370:7334
How can I write this in shell script?
Java regex shown in question would work in bash as well but make sure to not to use quoted regex variable. If the variable or string on the right hand side of =~ operator is quoted, then it is treated as a string literal instead of regex.
I also recommend using anchors in regex. Otherwise it will print matches for invalid input as: 2001:0db8:85a3:0000:0000:8a2e:0370:7334:foo:bar:baz.
Following script should work for you:
#!/bin/bash
regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
var="$1"
if [[ $var =~ $regex ]]; then
echo "matches"
else
echo "doesn't match!"
fi
[[ and =~ won't work with sh, and awk almost works everywhere.
Here is what I did
saved as ./check-ipv6.sh, chmod +x ./check-ipv6.sh
#!/bin/sh
regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
echo -n "$1" | awk '$0 !~ /'"$regex"'/{print "not an ipv6=>"$0;exit 1}'
Or you prefer bash than sh
#!/bin/bash
regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
awk '$0 !~ /'"$regex"'/{print "not an ipv6=>"$0;exit 1}' <<< "$1"
Test
~$ ./check-ipv6.sh 2001:0Db8:85a3:0000:0000:8a2e:0370:7334x
not an ipv6=>2001:0Db8:85a3:0000:0000:8a2e:0370:7334x
~$ echo $?
1
~$ ./check-ipv6.sh 2001:0Db8:85a3:0000:0000:8a2e:0370:7334
~$ echo $?
0

How to write and match regular expressions in /bin/sh script?

I am writing a shell script for a limited unix-based microkernel which doesn't have bash! the /bin/sh can't run the following lines for some reasons.
if [[ `uname` =~ (QNX|qnx) ]]; then
read -p "what is the dev prefix to use? " dev_prefix
if [[ $dev_prefix =~ ^[a-z0-9_-]+#[a-z0-9_-"."]+:.*$ ]]; then
For the 1st and 3rd lines, it complains about missing expression operator, and for the 2nd line it says no coprocess! Can anyone shed light on differences between /bin/bash and /bin/sh scripts?
You can use this equivalent script in /bin/sh:
if uname | grep -Eq '(QNX|qnx)'; then
printf "what is the dev prefix to use? "
read dev_prefix
if echo "$dev_prefix" | grep -Eq '^[a-z0-9_-]+#[a-z0-9_-"."]+:'; then
...
fi
fi
You can use shellcheck to detect non-Posix features in a script:
Copy/Paste this into https://www.shellcheck.net/:
#!/bin/sh
if [[ `1uname` =~ (QNX|qnx) ]]; then
read -p "what is the dev prefix to use? " dev_prefix
if [[ $dev_prefix =~ ^[a-z0-9_-]+#[a-z0-9_-"."]+:.*$ ]]; then
: nothing
fi
fi
Or install shellcheck locally, and run shellcheck ./check.sh,
and it will highlight the non-posix features:
In ./check.sh line 2:
if [[ `1uname` =~ (QNX|qnx) ]]; then
^-- SC2039: In POSIX sh, [[ ]] is not supported.
^-- SC2006: Use $(..) instead of deprecated `..`
In ./check.sh line 4:
if [[ $dev_prefix =~ ^[a-z0-9_-]+#[a-z0-9_-"."]+:.*$ ]]; then
^-- SC2039: In POSIX sh, [[ ]] is not supported.
You either have to rewrite the expressions as globs (not realistic), or use external commands (grep/awk), a explained by #anubhava

bash if [[ =~ regex compare not working?

I have a value in a variable that may be absolute or relative url, and I need to check which one it is.
I have found that there's a =~ operator in [[, but I can't get it to work. What am I doing wrong?
url="http://test"
if [[ "$url" =~ "^http://" ]];
then echo "absolute.";
fi;
You need to use regex without quote:
url="http://test"
if [[ "$url" =~ ^http:// ]]; then
echo "absolute."
fi
This outputs `absolute. as regex needs to be without quote in newer BASH (after BASH v3.1)
Or avoid regex and use glob matching:
if [[ "$url" == "http://"* ]]; then
echo "absolute."
fi

Regular expression alteration not matching in BASH

I have been trying to get multiple not matching alteration to work in a BASH.
This is what I am trying to not match. If there are two parameters and a switch(-a,-b,-c,-d) is the first parameter.
Example:
./scriptname -a filename
./scriptname -d filename
What I want this to echo success is for:
./scriptname filename ipaddress.
The code that works is :
if [[ "$#" = "2" && "$1" =~ ([^-a][^-b][^-c]) ]]
then
echo "success"
else
echo "fail"
fi
If I try to expand on the alteration with ([^-a][^-b][^-c][^-d]) it stops working. I have tried multiple syntax variants and nothing seems to work. I also tried to group them together like:
if [[ "$#" = "2" && "$1" =~ ([^-a][^-b]) && "$1" =~ ([^-c][^-d]) ]] and this fails as well.
What about:
if [[ "$#" = "2" && "$1" =~ -[a-d]$ ]]

shell test operator regular expressions

#!/bin/bash
# This file will fix the cygwin vs linux paths and load programmer's notepad under windows.
# mail : <sandundhammikaperera#gmail.com>
# invokes the GNU GPL, all rights are granted.
# check first parameter is non empty.
# if empty then give a error message and exit.
file=${1:?"Usage: pn filename"};
if [[ "$file" == /*/* ]] ;then
#if long directory name.
# :FAILTHROUGH:
echo "$0: Executing pn.exe $file"
else
file="$(pwd)/$file";
fi
#check whether the filename starts with / if so replace it with appropriate prefix #
prefix="C:/cygwin/";
#check for the partterns starting with "/" #
echo $var | grep "^/*$"
if [[ "$?" -eq "0" ]] ;then
# check again whether parttern starts with /cygdrive/[a-z]/ parttern #
if [[ $file == /cygdrive/[a-z]/* ]] ; then
file=${file#/cygdrive/[a-z]/};
file="C:/"$file;
else
file="$prefix""$file";
fi
fi
#check for the appropriate file permissions #
# :TODO:
echo $file
exec "/cygdrive/c/Program Files (x86)/Programmer's Notepad/pn.exe" $file
as I in my program which convert path names between cygwin and windows and load
the pn.exe [ programmer's notepad in windows]. So my questions are,
There are built in regex expression for the "[[" or 'test' operator. (as well as
I used them in my above program). But why they don't work in here if I change,
echo $var | grep "^/*$"
if [[ "$?" -eq "0" ]] ;then
to this,
if [[ "$file" == ^/*$ ]] ;then
What is the reason for that? Is there any workaround?
I have already tried the second method [[ "$file" == ^/*$ ]] but it didn't work.
then , simple googling brought to me here: http://unix.com/shell-programming
How to find all the documentation about [[ operator or 'test' command? I have used
man test but :(. Which document specifies it's limitations on regex usage if there so.
First, grep "^/*$" will only match paths containing only slashes, like "/", "///", "////". You can use grep "^/" to match paths starting with a slash. If you want to use bash regexes:
var="/some"
#echo $var | grep "^/"
if [[ "$var" =~ ^/ ]] ;then
echo "yes"
fi