Check single character in array bash for password generator - arrays

I am trying to test User input with password rules.
must be at least 8 characters long but not longer than 16 characters.
must contain at least 1 digit (0-9).
must contain at least 1 lowercase letter.
must contain at least 1 uppercase letter.
must contain exactly one and only one of #, #, $, %, &, *, +, -, =.
I'm trying to figure how I can compare the user string to the arrays that contain the password characters.
#!/bin/bash
Upper=("A""B""C""D""E""F""G""H""I""J""K""L""M""N""O""P""Q""R""S""T""U""V""W""X""Y""Z")
Lower=("a""b""c""d""e""f""g""h""i""j""k""l""m""o""p""q""r""s""t""u""v""w""x""y""z")
Numbers=("1""2""3""4""5""6""7""8""9")
SpecialChar=("&""#""#""$""%""*""-""+""=")
# Password Generator
PassGen(){ # generate password if no user input
Pwlength=`shuf -i 8-16 -n 1` # holds the range/length of password
Password=`< /dev/urandom tr -dc A-Za-z0-9$SpecialChar | head -c $Pwlength`
echo "Random Password is being generated for you"
sleep 5
echo "Your new password is : $Password"
echo exit?
}
# I want to make the following to work :
\#PassGen2(){ # generate password 2 if no user input
\#if [ $# -eq 0 ] ; then
\#for ((i=0;i<4;i++))
\#do
\#password="${Upper[$random % ${#Upper[#]}" ] }
\#password="${Lower[$random % ${#Lower[#]}" ] }
\#password="${Numbers[$random % ${#Numbers[#]}" ] }
\#done
\#password="${SpecialChar[$random % ${#SpecialChar[#]}" ] }
\#password="${Upper[$random % ${#Upper[#]}" ] }
\#echo "Random Password is being generated for you"
\#sleep 5
\#echo "Your new password is : $Password"
\#fi
\#echo exit?
\#}
# Help menu
Usage(){ # Help menu
echo "The password must meet the following :"
echo "> must be at least 8 characters long but not longer than 16 characters."
echo "> must contain at least 1 digit (0-9)."
echo "> must contain at least 1 lowercase letter."
echo "> must contain at least 1 uppercase letter."
echo "> must contain exactly one and only one of # # $ % & * + - ="
echo ""
echo " * * IF NO PASSWORD IS ENTERED THE SYSTEM WILL GENERATE A PASSPORD FOR YOU * * "
echo exit?
}
#The main starts here
if [ $# -eq 0 ] ; then
PassGen $SpecialChar
fi
if [ $# == "-h"] ; then
Usage
fi
UserPW=$1 # assigning the firsth positional parameter to variable (password)
#The reason I open the post
PwCheck(){
if [[ ${#UserPW} < 8 && ${#UserPW} > 0 ]] ;then #check the lower end of the password
echo "Password have to be more then 8 character"
exit 1
fi
if [[ ${#UserPW} < 16 ]] ;then #checks if the password bigger then 16 character
echo "Password too long ! - Password have to be between 8 - 16 characters"
exit 1
fi
if [[ ${#UserPW} < 17 && ${#UserPW} > 8 ]] ; then
Pwarr=($UserPW) # putting the variable into Array
for (( i=0; i < ${#Upper[#]};i++ ))
do
if [[ ${Pwarr[0]:[i]:1} != ${Upper[i]} ]]; then
echo "You have to enter at least 1 upper character letter"
exit 1
fi
done
for (( i=0; i < ${#Lower[#]};i++ ))
do
if [[ ${Pwarr[0]:[i]:1} != ${Lower[i] ]]; then
echo "You have to enter at least 1 Lower character letter"
exit 1
fi
done
for (( i=0; i < ${#SpecialChar[#];i++ ))
do
if [[ ${Pwarr[0]:[i]:1} != ${SpecialChar[i] ]] ;then
echo " You have to enter at least 1 special character
exit 1
fi
done
#}
My problems:
I couldn't make my PassGen2() to work beside "calling it". I know
there must be typo or syntax errors.
For PwCheck() function I know I didn't call it and the IF
statement should be in the main section.

It's easier to implement PwCheck leaving the user password as a string rather than converting it to an array:
bad_length(){
l=`wc -c`
[ $l -ge 8 ] && [ $l -le 16 ] && return 1 || return 0
}
no_digit(){
grep -q '[0-9]' && return 1 || return 0
}
no_lc(){
grep -q '[a-z]' && return 1 || return 0
}
no_uc(){
grep -q '[A-Z]' && return 1 || return 0
}
specials='##$%&*+=-'
no_exactly_one_special(){
n=`grep -o "[$specials]" | wc -l`
[ $n -eq 1 ] && return 1 || return 0
}
echo "$1" | bad_length && echo 'Password must be between 8 - 16 characters' && exit 1
echo "$1" | no_digit && echo 'Password must have at least one number 0-9' && exit 1
echo "$1" | no_lc && echo 'Password must have at least one lowercase letter' && exit 1
echo "$1" | no_uc && echo 'Password must have at least one UPPERCASE letter' && exit 1
echo "$1" | no_exactly_one_special && echo "Password must have exactly one special character: $specials" && exit 1
exit 0
A few words of explanation:
First, it is possible in bash to pipe (using |) to a function:
no_digit_from_stdin(){
grep -q '[0-9]' && return 1 || return 0
}
echo "abc" | no_digit_from_stdin && echo $?
instead of passing it as an argument:
no_digit_from_arg(){
echo "$1" | grep -q '[0-9]' && return 1 || return 0
}
no_digit_from_arg "abc" && echo $?
These two have the same outcome here.
Second, it is possible to use pipes and && to control flow concisely. So this:
echo "$1" | no_digit_from_stdin && echo 'Password must have at least one number 0-9' && exit 1
has the same outcome as this:
if no_digit_from_arg "$1"; then
echo 'Password must have at least one number 0-9'
exit 1
fi
...but these are matters of style and not directly relevant to your question.
Here is a version of my answer that you may find more usable, with a secret bonus feature:
PwCheck(){
pass=true
l=`echo "$UserPW" | wc -c`
if [ $l -lt 8 ] || [ $l -gt 16 ]; then
echo 'Password must be between 8 - 16 characters'
pass=false
fi
if ! echo "$UserPW" | grep -q '[0-9]'; then
echo 'Password must have at least one number 0-9'
pass=false
fi
if ! echo "$UserPW" | grep -q '[a-z]'; then
echo 'Password must have at least one lowercase letter'
pass=false
fi
if echo "$UserPW" | grep -q '[A-Z]'; then
echo 'Password must have at least one UPPERCASE letter'
pass=false
fi
specials='##$%&*+=-'
n=`echo "$UserPW" | grep -o "[$specials]" | wc -l`
if [ $n -ne 1 ]; then
echo "Password must have exactly one special character: $specials"
pass=false
fi
if $pass; then
exit 0
else
exit 1
fi
}

Related

How to use an argument in a loop definition

thanks for your help.
What a I doing wrong, here ? I want the numeric answer to be used in a loop but it does not return what I expected :
if [ "$#" -eq 0 ]; then
echo -n "Enter the number: "
read answer
else
answer=( "$#" )
fi
for i in {1..$answer} ; do echo ${i}; done
When executed, I have this:
me#local misc]$ ./test.sh
Enter the number: 5
{1..5}
I expected the echo to return 1 2 3 4 5
You can also do it like:
if [ "$#" -eq 0 ]; then
echo -n "Enter the number: "
read answer
else
answer=( "$#" )
fi
for i in $(seq 1 $answer) ; do echo ${i}; done
With no proposed answer I decided to switch to bash with that syntax that works (though it bothers me)
for (( i=1 ; i<=${answer} ; i++));

Compare array elements with an element

I am trying to compare an array with a value and print an error statement if the match is not found.
arraylist="$(ls new-dir/ | cut -d' ' -f1)"
For example, this stores values such as small, large and medium which are the files present in new-dir.
The value to be compared with will be entered by the user viz. var
I have tried something like following:
(for i in "${arraylist[#]}"; do [[ "$i" == "$var"]] && exit 0; done) && echo "found" || echo "not found"
Also tried, however, doesn't work:
arraylist="$(ls new-dir/ | cut -d' ' -f1)"
count=0
for((i=0; i<${#arraylist[#]}; i++)); do
if [ "$arraylist[$i]" == "$var" ] ; then
count=1
fi
done
if [ $count -eq 0 ]; then
echo "Not found"
fi
Is there any other way to do this comparison?
This code works. Just replace arraylist with your array.
arraylist=('hello' 'world')
var='hello'
count=0
for i in "${arraylist[#]}"
do
if [ "$i" == "$var" ]; then
count=1
fi
done
if [ $count -eq 0 ]; then
echo "Not found"
fi
To extract the first "word" from each filename, I'd do
declare -A prefixes
cd new-dir
for file in *; do
read -r prefix _ <<< "$file"
prefixes["$prefix"]=1
done
cd -
then to look for a match:
# $var is somehow provided
result=""
for p in "${!prefixes[#]}"; do
if [[ $p == "$var" ]]; then
result=found
break
fi
done
echo "${result:-not found}"
This requires bash v4.0+ for the use of an associative array.
If you have bash v4.3+ you can do
[[ -v prefixes[$var] ]] && echo found || echo not found
Alternately:
shopt -s nullglob
files=( new-dir/"$var "* )
(( ${#files[#]} > 0 )) && echo found || echo not found
This is what shell programming is about. Keep it simple :
Array1=( "item1" "item2" "item3" "item-4" )
var="item3"
count=$(echo ${Array1[#]} | tr ' ' '\n' | awk '$1 == "'"$var"'"{print $0}' | wc -l)
[ $count -eq 0 ] && echo "Not found" || echo "found"
The arraylist might be an array with just one index.
The command:
arraylist="$(ls new-dir/ | cut -d' ' -f1)"
might just assign whole "ls new-dir" output to ${arraylist[0]}
You can check the index value in an array by echo ${arraylist[1]} (to confirm).
Try changing the command to generate an array for a list of files/dirs to:
arraylist=($(ls new-dir/))
The complete script:
arraylist=($(ls new-dir/))
(for i in "${arraylist[#]}"; do [[ "$i" == "$var" ]] && exit 0; done) && echo "found" || echo "not found"
You can also ignore the whole array assignment and just use subshell-command in for loop, eg:
(for i in $(ls -1 new-dir/); do [[ "$i" == "$var" ]] && exit 0; done) && echo "found" || echo "not found"
Remember, no quotes " around $(ls -1 new-dir/)
Another one-liner without any array or loops:
ls -1 new-dir/ | grep -q "$var" && echo "found" || echo "not found"
Edit:
As #M. Nejat Aydin suggested, use [[ -e new-dir/$var ]] && echo "found" || echo "Not found" and do not use ls in the script.

Here am getting error as operand expected,error token is +

#!/bin/bash -x
echo "enter an integer "
read $val
for((i=0;i<=2;i++))
do
Numbers[$i]=$val
done
echo ${Number[#]}
if [ $((${Number[0]}+${Number[1]}+${Number[2]})) -eq 0 ]
then
echo "Sum is Zero"
else
echo "Sum is not zero"
fi
Couple of problems:
#!/bin/bash -x
echo "enter an integer "
read val # read takes the name of a variable
for((i=0;i<=2;i++))
do
Numbers[$i]=$val
done
echo ${Numbers[#]} # from here on, you wrote "Number" instead of "Numbers"
if [ $((Numbers[0] + Numbers[1] + Numbers[2])) -eq 0 ] # you don't need $ to read variables in arithmetic expansions
then
echo "Sum is Zero"
else
echo "Sum is not zero"
fi

Fetching data into an array

I have a file like this below:
-bash-4.2$ cat a1.txt
0 10.95.187.87 5444 up 0.333333 primary 0 false 0
1 10.95.187.88 5444 up 0.333333 standby 1 true 0
2 10.95.187.89 5444 up 0.333333 standby 0 false 0
I want to fetch the data from the above file into a 2D array.
Can you please help me with a suitable way to put into an array.
Also post putting we need put a condition to check whether the value in the 4th column is UP or DOWN. If it's UP then OK, if its down then below command needs to be executed.
-bash-4.2$ pcp_attach_node -w -U pcpuser -h localhost -p 9898 0
(The value at the end is getting fetched from the 1st column.
You could try something like that:
while read -r line; do
declare -a array=( $line ) # use IFS
echo "${array[0]}"
echo "${array[1]}" # and so on
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
Or:
while read -r -a array; do
if [[ "$array[3]" ]]; then
echo execute command...
fi
done < a1.txt
This works only if field are space separated (any kind of space).
You could probably mix that with regexp if you need more precise control of the format.
Firstly, I don't think you can have 2D arrays in bash. But you can however store lines into a 1-D array.
Here is a script ,parse1a.sh, to demonstrate emulation of 2D arrays for the type of data you included:
#!/bin/bash
function get_element () {
line=${ARRAY[$1]}
echo $line | awk "{print \$$(($2+1))}" #+1 since awk is one-based
}
function set_element () {
line=${ARRAY[$1]}
declare -a SUBARRAY=($line)
SUBARRAY[$(($2))]=$3
ARRAY[$1]="${SUBARRAY[#]}"
}
ARRAY=()
while IFS='' read -r line || [[ -n "$line" ]]; do
#echo $line
ARRAY+=("$line")
done < "$1"
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
for line in "${ARRAY[#]}"; do
#echo $line
if [ "$(echo $line | awk '{print $4}')" == "down" ]; then
echo "Replace this with what to do for down"
else
echo "...and any action for up - if required"
fi
done
echo "Element access of [2,3]:"
echo "get_element 2 3 : "
get_element 2 3
echo "set_element 2 3 left: "
set_element 2 3 left
echo "get_element 2 3 : "
get_element 2 3
echo "Full array contents printout:"
printf "%s\n" "${ARRAY[#]}" # Full array contents printout.
It can be executed by:
./parsea1 a1.txt
Hope this is close to what you are looking for. Note that this code will loose all indenting spaces during manipulation, but a formatted update of the lines could solve that.

In array operator in bash

Is there a way to test whether an array contains a specified element?
e.g., something like:
array=(one two three)
if [ "one" in ${array} ]; then
...
fi
A for loop will do the trick.
array=(one two three)
for i in "${array[#]}"; do
if [[ "$i" = "one" ]]; then
...
break
fi
done
Try this:
array=(one two three)
if [[ "${array[*]}" =~ "one" ]]; then
echo "'one' is found"
fi
I got an function 'contains' in my .bashrc-file:
contains ()
{
param=$1;
shift;
for elem in "$#";
do
[[ "$param" = "$elem" ]] && return 0;
done;
return 1
}
It works well with an array:
contains on $array && echo hit || echo miss
miss
contains one $array && echo hit || echo miss
hit
contains onex $array && echo hit || echo miss
miss
But doesn't need an array:
contains one four two one zero && echo hit || echo miss
hit
I like using grep for this:
if echo ${array[#]} | grep -qw one; then
# "one" is in the array
...
fi
(Note that both -q and -w are non-standard options to grep: -w tells it to work on whole words only, and -q ("quiet") suppresses all output.)
array="one two three"
if [ $(echo "$array" | grep one | wc -l) -gt 0 ] ;
then echo yes;
fi
If that's ugly, you could hide it away in a function.
if you just want to check whether an element is in array, another approach
case "${array[#]/one/}" in
"${array[#]}" ) echo "not in there";;
*) echo "found ";;
esac
In_array() {
local NEEDLE="$1"
local ELEMENT
shift
for ELEMENT; do
if [ "$ELEMENT" == "$NEEDLE" ]; then
return 0
fi
done
return 1
}
declare -a ARRAY=( "elem1" "elem2" "elem3" )
if In_array "elem1" "${ARRAY[#]}"; then
...
A nice and elegant version of the above.
in_array() {
local needle=$1 el
shift
for el in "$#"; do
if [ "$el" = "$needle" ]; then
return 0
fi
done
return 1
}
if in_array 1 1 2 3; then
echo true
else
echo false
fi
# alternatively
a=(1 2 3)
if in_array 1 "${a[#]}"; then
...
OPTIONS=('-q','-Q','-s','-S')
find="$(grep "\-q" <<< "${OPTIONS[#]}")"
if [ "$find" = "${OPTIONS[#]}" ];
then
echo "arr contains -q"
fi

Resources