Shell, if statment of variable only containing alphabet? [duplicate] - shell

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

Related

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

Linux Regular Expression

I'm working with shell scripting in Linux. I want to check if the value of MAX_ARCHIVE_AGE is numeric or not. My code is like this:
MAX_ARCHIVE_AGE = "50"
expr="*[0-9]*"
if test -z "$MAX_ARCHIVE_AGE";
then
echo "MAX_ARCHIVE_AGE variable is missing or not initiated"
else
if [ "$MAX_ARCHIVE_AGE" != $expr ]
then
echo "$MAX_ARCHIVE_AGE is not a valid value"
fi
fi
I want to match the value of MAX_ARCHIVE_AGE with my expr. Please help.
For POSIX compatibility, look at case. I also find it more elegant than the corresponding if construct, but the syntax may seem a bit odd when you first see it.
case $MAX_ARCHIVE_AGE in
'' ) echo "empty" >&2 ;;
*[!0-9]* ) echo "not a number" >&2 ;;
esac
By the way, notice the redirection of error messages to standard error with >&2.
Your expr will match anything that contains any digits; it's better to check if it contains only digits, or conversely, to check if it contains any non-digits. To do that, you can write:
if ! [[ "$MAX_ARCHIVE_AGE" ]] ; then
echo "MAX_ARCHIVE_AGE is blank or uninitialized" >&2
elif [[ "$MAX_ARCHIVE_AGE" == *[^0-9]* ]] ; then
echo "$MAX_ARCHIVE_AGE is not a valid value" >&2
fi
Also, note that you would initialize MAX_ARCHIVE_AGE by writing e.g. MAX_ARCHIVE_AGE=50 (no spaces), not MAX_ARCHIVE_AGE = 50. The latter tries to run a program called MAX_ARCHIVE_AGE with the arguments = and 50.

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

How to validate filename in bash script?

A little bit about my application:
I am writing a small application in bash script. The application must store personal settings to home directory.
My settings are in the form of a key/value pair which will be stored as filename/content:
for example:
~/my-github-app
├── github-account
├── github-token
My current solution for adding a key/value:
read KEY
read VALUE
# FIXME! should check for for valid filename.
# user can do injection hack by KEY="../../path/to/yourfile"
echo $VALUE > ~/my-github-app/$KEY
What is the simplest and safe way to validate $KEY?
A built-in function?
A regular expression?
I really need a reusable solution, not just for this application.
Edit:
"validate filename" mean check string for proper filename format, accepted by OS.
"bob" : good filename format
"" : bad because filename can not be empty
"*" : ?
"/" : ?
"con" : ?
....
The only way to make something secure is to use a whitelist. Which means instead of blacklisting bad characters you allow good ones. The reason why blacklists will always fail is that you can't blacklist all of the weird characted, you'd always forget something. Especially if you're working with unicode strings.
Filenames could contain anything. According to wikipedia:
Ext4 Allowed characters in filenames: All bytes except NUL ('\0') and '/'
Which means that whole bash scripts could be valid filenames.
So, if I were you, I would only allow a-zA-Z as valid characters. Problem solved.
That's how you do it:
# if [[ $key =~ [^a-zA-Z] ]]; then # or this. Whatever makes more sense to you
if ! [[ $key =~ ^[a-zA-Z]+$ ]]; then
echo 'Wrong key. Only a-zA-Z characters are allowed' >&2 # write to stderr
exit 1
fi
In addition to #Aleks-Daniel Jakimenko-A.'s answer, following script checks following conditions, If all conditions are set then True is returned:
a-z
A-Z
0-9
underscore (_)
dash (-)
period (.)
Max length is 255
First character should be a character or number: {a-z or A-z or 0-9}
my_script.sh:
#!/bin/bash
# To run: bash my_script.sh <my_string_is>
key=$1
val=$(echo "${#key}")
if [[ $key == "" ]]; then
echo "False";
exit
fi
if [[ $key == "." ]] || [[ $key == ".." ]]; then
# "." and ".." are added automatically and always exist, so you can't have a
# file named . or .. // https://askubuntu.com/a/416508/660555
echo "False";
exit
fi
if [ $val -gt 255 ]; then
# String's length check
echo "False";
exit
fi
if ! [[ $key =~ ^[0-9a-zA-Z._-]+$ ]]; then
# Checks whether valid characters exist
echo "False";
exit
fi
_key=$(echo $key | cut -c1-1)
if ! [[ $_key =~ ^[0-9a-zA-Z.]+$ ]]; then
# Checks the first character
echo "False";
exit
fi
echo "True";
If you just want to check if a file already exists, use the test command and use it like this for your validation :
if [[ ! -e "$KEY" ]]
then
#file doet not exists
fi
What do you want to validate, just that a key doesn't contain any path info?
KEY=$(basename $KEY)
This would remove any parts of the KEY that are part of the path. That said, there are plenty of things the user could enter that would probably be a bad idea. Can you perhaps have a list of allowed keys, then reject anything that isn't in that list?
If you're trying to see if a file is writable, you could check if a) it exists and is writable (-w) or b) just try to write to it and check for errors.

BASH if comparison to force a valid e-mail address format is entered

I have a useradd bash script which requests the user enter an e-mail address for the user being created. This is so the user receives his username/password in an e-mail when his/her account is created.
Currently this part of the code is very simple:
echo Enter the users e-mail address
read ADDRESS
What i'm finding is that sometimes when the operators run the script they are entereing blank information. How can I put a if statement in place that enforces they enter an e-mail address format.
I tried the following code but it doesn't work. The idea was to at least verify they are using the # symbol.
if [[ $string != "#" ]] ; then
echo You have entered an invalid e-mail address!
exit 1
else
# do something
fi
If you're just looking for something quick and dirty, this bash conditional expression will match something that has at least one char, an '#', at least one char, a dot, and at least one char.
[[ "$email" == ?*#?*.?* ]]
Examples
$ [[ "a#b.c" == ?*#?*.?* ]] && echo Y || echo n
Y
$ [[ "foo#bar" == ?*#?*.?* ]] && echo Y || echo n
n
Actual email validation is gnarly (see here)
!= tests for exact inequality: the string would have to be exactly # with nothing else. Two ways to do the test you want are
case "$string" in
*#*)
;;
*)
echo You have entered an invalid email address! >&2
exit 1
;;
esac
or
if ! expr "$string" : '.*#' >/dev/null; then
echo You have entered an invalid email address! >&2
exit 1
fi
You need to redirect the result from expr because it will print the matched length. Note also that case uses shell globs, whereas expr uses POSIX basic regular expressions (so you can't use +, ?, etc.); and you need to quote the regex passed to expr so the shell doesn't expand it, but for case the whole point is to have the shell expand it.
I generally prefer the case one unless I actually need a regex.
You could e.g. use bash's =~ operator, e.g.:
if [[ $string =~ "#" ]] ; then
# do something
else
echo You have entered an invalid e-mail address!
exit 1
fi
You can use glob-style patterns in if conditionals in bash:
if [[ $string != *"#"* ]] ; then
echo You have entered an invalid e-mail address!
exit 1
else
# do something
fi
I'd go a step further and require at least one character at either side of the #:
if [[ $string != *?"#"?* ]] ; then
echo You have entered an invalid e-mail address!
exit 1
else
: # do something
fi

Resources