Bash regex does not accept slash - regex

i am pretty new to bash shell scripting (and linux too)... i try to do a simple script which involves some regex for a string given by keyboard from a user.
clear
read -p "Insert e-mail > "
if [[ $REPLY =~ ^[.] ]]
then
echo "ERROR (code 1): e-mail cannot start with \".\""
elif [[ $REPLY =~ .[.]$ ]]
then
echo "ERROR (code 2): e-mail cannot end with \".\""
else
if [[ $REPLY =~ ^[0-9][0-9a-zA-Z!#$%^\&\'*+-]+$ ]] #THIS IS WHERE I NEED HELP
then
echo "Good!"
else
echo "Bad!"
fi
fi
so what i want to do is to make a regex
so that the user cant start with . or end with . (i pretty much did that and its working)...
next what i wanted to do was make the string start with a number and i did that with ^[0-9] (i think this is correct)
and after that..string could be anything like a number 0-9 or letters a-z and A-Z or the next characters: !#$%^&'*+-/
so when user entered 1& (it starts with number and the rest is in the acceptable characters) but it didn't work.. because it need to be \& (at the regex formula).
next the same problem occurred to character ' what i did, was to add again a backslash to regex formula (\') and it worked..
then i tried to do the same with / character (slash character) so what i did was add a backslash / (backslash slash) but when user entered 1/ (it starts with number and the rest are acceptable characters) unfortunately it printed "Bad!" ... it should print Good!..
why is that happening?
i tried \/ and \\/ but still... cant understand why it doesn't work!

Problem is presence of ! in your character class that is doing history expansion.
I suggest declaring your regex beforehand like this:
re="^[0-9][0-9a-zA-Z\!#$%^&/*'+-]+$"
Then use it as:
s='1/'
[[ $s =~ $re ]] && echo "good" || echo "bad"
good

Actually, /s work in character classes just fine:
$ [[ "1/" =~ ^[0-9][/]+$ ]]; echo $?
0

Related

Bash Regex to check if first character of string is a number

I'm writing a script that uses trace route. I'm iterating through each line of the trace route and then through each word (separated by whitespace). However, sometimes the trace route returns a * character, which causes issues when echoing (filenames are output).
I've been fiddling with RegEx and so far I've come up with this:
if [[ $item =~ ^\d ]];
Item is a portion of the trace route.
For each item in a trace route line, I would simply like to check if the first character is equal to a number or not, then continue accordingly.
\d is not supported in POSIX Regular Expressions (used by Bash). You need to replace it with [0-9] like so:
if [[ $item =~ ^[0-9] ]];
Check out this StackOverflow answer
Could also use [:digit:] to make it easier to read:
if [[ $item =~ ^[[:digit:]] ]];
No need to use regex just glob is sufficient:
[[ $item == [0-9]* ]] && echo "it starts with a digit"
You can also use:
[[ $item == [[:digit:]]* ]]

shell script odd regex

i have some regex that is behaving oddly in my shell script i have variables, and i have tried every what way to get them to behave, and they dont seem to do any regex, and i know my regex quite well thanks to regex101, here is what a sample looks like
fname="direcheck"
FIND="*"
if [[ $fname =~ $FIND ]]; then
echo "no quotes"
fi
if [[ "$fname" =~ "$FIND" ]]; then
echo "with quotes"
fi
right now it will display nothing
if i change find to
FIND="[9]*"
then it prints no quotes
if i say
FIND="[a-z]*"
then it prints no quotes
if i say
FIND="dircheck"
then nothing prints
if i say
FIND="*ck"
then nothing prints
I don't get how this regex is working
how do i use these variables, and what is the proper syntax?
* and *ck are invalid regular expressions. It would work (with no quotes) if you were comparing with ==, not =~. If you want to use the same functionality that you get in == for them, the equivalent regexps are .* and .*ck.
[9]* is any number (including zero) of characters that are 9. There is zero characters 9 in your direcheck, so it matches. (Edited from brainfart, thanks chepner)
dircheck is not found in direcheck, so not printing anything is hardly surprising.
[a-z]* is any number of characters that are between a and z (i.e. any number of lowercase letters). This will match, assuming it's not quoted.
I finally figured it out, and why it was working so oddly
[a-z]* and [9]* and [anythinghere]* they all match because it matches zero or more times. so "direcheck" has [9] zero or more times.
so
if [[ "$fname" =~ $FIND ]]; then
or
if [[ $fname =~ $FIND ]]; then
are both correct, and
if [[ "$fname" =~ "$FIND" ]]; then
matches only when the string matches exactly because $FIND is matched as a literal string not regex

Matching exactly one whitespace inside if statement

I'm trying to match a file right now to change the name of the file
tempString="hi"
end="_hi.pdf"
for c in *.pdf; do
tempString="$(echo ${c})"
if [[ $tempString =~ $AA[0-9][0-9][0-9]\.pdf$ ]]
then
echo "inside if"
tempString="$(echo $tempString | tr -d ' ')"
tempString=${tempString%.pdf}
mv "%c" "$monthyear$tempString$end"
fi
done
tempString is set to something like "AA 111.pdf"
i need it to match something like AA 111.pdf but not AA 111.pdf (one space instead of two spaces). I just want it to match exactly one whitespace inbetween AA and 111.
it keeps matching both of those examples or neither. i've tried \s, [\s], [:space:], [[:space:]], etc.
i've tried looking it up everywhere but to no avail. can somebody help me out?
The following will match one (and only one) space names like AA 111.pdf:
if [[ "$tempString" =~ ^AA" "[0-9][0-9][0-9]\.pdf$ ]];
The trick is to quote your spaces inside the regex.
Update: The following code ignores the two (and more) spaces example:
tempString="AA 111.pdf"
if [[ "$tempString" =~ ^AA" "[0-9][0-9][0-9]\.pdf$ ]]; then
echo "yes"
else
echo "no"
fi
This prints no
One-liner version:
tempString="AA 111.pdf"; if [[ "$tempString" =~ ^AA" "[0-9][0-9][0-9]\.pdf$ ]]; then echo "yes"; else echo "no"; fi
try this regex [a-zA-z]+ \d+
Demo
and if you want any character before space use this \w+ \d+
and if you want any character before and after space use this \w+ \w+
if you want to take file extension into consideration you can add \.pdf$
at the end of any regex from the above

IF statement in BASH isn't doing what's expected

Got a very simple script which checks barcodes basically. There's two barcodes that have to be checked that they are not confused when being made into a variable.
Basically the first barcode should contain only numbers 0-9, and the second barcode should contain two letters, then some numbers, then two more letters, like AB123456789CD.
If they're confused and read in the wrong order then it plays an error sound. This is what I have so far, the top one's working, but I'm not sure it it's the best solution, and the bottom one doesn't do what I want:
echo -e $BLUE"Please scan the first barcode"$ENDCOLOUR
read -p "Barcode: " BARCODE1
if [[ "$BARCODE1" =~ [a-z] ]] ; then
play -q ./error.wav
else
echo -e $BLUE"Please scan the second barcode"$ENDCOLOUR
read -p "Barcode: " BARCODE1
if [[ "$BARCODE2" =~ [a-z0-9] ]] ; then
play -q ./error.wav
else
echo "'$BARCODE1',$BARCODE2'" >> barcodes.csv
fi
fi
What's wrong? And is there a more optimal means of achieving this?
Only numbers:
if ! [[ $BARCODE1 =~ ^[0-9]+$ ]]; then
Because of the + sign this is going to enter the if statement for empty strings as well. + means one or more times and * means zero or more time.
Two characters, numbers, two characters:
if ! [[ $BARCODE1 =~ ^[a-zA-Z][a-zA-Z][0-9]+[a-zA-Z][a-zA-Z]$ ]]; then
Once again, this is not going to match for strings like 'AABB'. If you think that 'AABB' is a valid barcode, then use this:
if ! [[ $BARCODE1 =~ ^[a-zA-Z][a-zA-Z][0-9]*[a-zA-Z][a-zA-Z]$ ]]; then
EDIT:
Also, if you know exact count of numbers in a barcode, then you could use {n}
if ! [[ $BARCODE1 =~ ^[a-zA-Z]{2}[0-9]{9}[a-zA-Z]{2}$ ]]; then
Which means 2 letters, 9 numbers, 2 letters

Bash need to test for alphanumeric string

Trying to verify that a string has only lowercase, uppercase, or numbers in it.
if ! [[ "$TITLE" =~ ^[a-zA-Z0-9]+$ ]]; then echo "INVALID"; fi
Thoughts?
* UPDATE *
The variable TITLE currently only has upper case text so it should pass and nothing should be outputted. If however I add a special character to TITLE, the IF statement should catch it and echo INVALID. Currently it does not work. It always echos invalid. I think this is because my regex statement is wrong. I think the way I have it written, its looking for a title that has all three in it.
Bash 4.2.25
The idea is, the user should be able to add any title as long as it only contains uppercase, lowercase or numbers. All other characters should fail.
* UPDATE *
If TITLE = ThisIsAValidTitle it echos invalid.
If TITLE = ThisIs#######InvalidTitle it also echos invalid.
* SOLUTION *
Weird, well it started working when I simplified it down to this:
TEST="Valid0"
if ! [[ "$TEST" =~ [^a-zA-Z0-9] ]]; then
echo "VALID"
else
echo "INVALID"
fi
* REAL SOLUTION *
My variable had spaces in it... DUH
Sorry for the trouble guys...
* FINAL SOLUTION *
This accounts for spaces in titles
if ! [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then
echo "VALID"
else
echo "INVALID"
fi
I'd invert the logic. Test for invalid characters and echo a warning if at least one is present:
if [[ "$TITLE" =~ [^a-zA-Z0-9] ]]; then
echo "INVALID"
fi
With that said, your original check worked for me, so you probably need to provide more context (i.e. a larger portion of your script).
why cant we use alnum
[[ 'mystring123' =~ [:alnum:] ]] && echo "ok" || echo "no"
the nominated answer is wrong. Because it doesn't check to the end of the string. also it's inverted. as the conditional says: "if the start of the string is valid characters then echo invalid"
[[ $TITLE =~ ^[a-zA-Z0-9_-]{3,20}$ ]] && ret="VALID" || ret="INVALID"
echo $ret