BASH Script syntax error using grep on variable containing special characters - bash

#!/bin/bash
password=$1
if [[ $# -gt 1 || $1 = "-h" ]]; then
echo 'Usage: pw | pw -h | pw password'
echo "Note: Valid passwords must be between 8-16 characters long.
contain at least 1 digit
contain at least 1 lowercase letter
contain at least 1 uppcase letter
contain one of # # $ % & * + - ="
fi
if [[ $# -lt 1 ]]; then
regex='0-9A-Z#*+#$%&a-z'
password=$( cat /dev/urandom | tr -dc '0-9A-Z#*+#$%&a-z' | head -c $(( 8 + $RANDOM % 8 )))
check=$( echo $password | grep -o ['#*+#$%&'] | wc -m )
while [[ $check -gt 2 || $check -lt 1 ]]
do
password=$( cat /dev/urandom | tr -dc '0-9A-Z#*+#$%&a-z' | head -c $(( 8 + $RANDOM % 8 )))
check=$( echo $password | grep -o ['#*+#$%&'] | wc -m )
done
echo $password
fi
if [[ $# = 1 ]]; then
password=$1
echo "$password" | grep [A-Z][a-z]
if [[ $? = 1 ]]; then
echo "Password must contain at least one upper or lowercase characer"
exit 2
fi
echo "$password" | grep [0-9]
if [[ $? = 1 ]]; then
echo "Password must contain at least one digit"
exit 3
fi
if [[ `echo "$password" | grep -o ['##$%&*+-'] 2>/dev/null` -gt 2 ]]; then
echo "You must only use one special character"
exit 4
fi
if [[ `echo "$password" | grep -o ['\#\#\$\%\&\*\+\-'] 2>/dev/null` -lt 1 ]]; then
echo "Password must contain at least one special character"
exit 5
fi
woc=$( echo "$password" | wc -m )
if [[ $woc -lt 8 || $woc -gt 16 ]]; then
echo "Password must be between 8 and 16 characters"
exit 6
fi
echo "$1 is a valid password"
fi
The first part of this script works perfectly and generates a random password 8-16 characters long with only one special character. Problems are on line 40 and 45, both giving me the same error "Syntax error: expecting operand "[special character]". I have tried creating variables and calling them, using the $? variable to check output and more. Unfortunately this error stays consistently even though the script does run correctly.

I fixed the code, it was a simple mistake. I forgot to include wc -m.
if [[ `echo "$password" | grep [\#\#\$\%\&\*\+\-] | wc -m` -lt 2 ]]

Related

Logical OR evaluates all expression in Bash script

Consider this simple shell script:
#!/bin/bash
exp1="[ $1 -gt 5 ]";
exp2="[ `ping localhost -c 4 | wc -l` -gt 0 ]";
if eval $exp1 || eval $exp2
then
echo Ok!
fi
I was expecting that passing 10 to this script, the ping was not executed, but it is clearly executed (since the script takes some seconds to write "Ok!").
I was wondering if there is a way to avoid/optimize this behavior?
It will execute ping because you have:
exp2="[ `ping localhost -c 4 | wc -l` -gt 0 ]";
which uses command substitution before evaluating the condition due to double quotes.
Use single quotes to delay the execution until the eval line, like so:
#!/bin/bash
exp1='[[ $1 -gt 5 ]]'
exp2='[[ $(ping localhost -c 4 | wc -l) -gt 0 ]]'
if eval $exp1 || eval $exp2
then
echo Ok!
fi
Better yet, use functions. Don't store commands in variables!
exp1() { [[ $1 -gt 5 ]]; }
exp2() { [[ $(ping localhost -c 4 | wc -l) -gt 0 ]]; }
if exp1 "$1" || exp2
then
echo Ok!
fi

Strange syntax error in if condition - Shell Script

So I'm making a shell script in Ubuntu. It's purpose is simple. You give a command with arguments and you get a different operation each time. The problem is that when I run the the script it won't actually run because of a syntax error in one elif. The most suspicious thing is that I have a similar elif above wich works or at least doesn't pop a syntax error...
I'm leaving my code for you to see it and understand. Thanks in advance!
if [ "$1" = "-a" -a $# -lt 3 ]
then
echo "Add a new line in katalogos!"
read -p "Give me a name... " name
read -p "Give me a surname... " surname
read -p "Give me a city name... " cityName
read -p "Give me a phone number... " num
echo "$name $surname $cityName $num" > katalogos
elif [ "$1" = "-l" -a $# -lt 3 ]
then
echo "Content of katalogos will be sorted numerically and blank lines will be excluded!"
sort -b -n katalogos
elif [ "$1" = "-s" -a $# -lt 4 ]
if [[ $2 != *[!0-9]* ]]
then
echo "Content of katalogos will be sorted according to the second argument!"
sort +$3 katalogos
fi
elif [ "$1" = "-c" -a $# -lt 4 ] // syntax error
if [[ $2 = *[!0-9]* ]]
then
echo "Content of katalogos will be sorted according to the keyword!"
if [ $(grep -e "$2" katalogos | wc -l) -eq 0 ]
then
echo "String is not matched."
else
grep -e "$2" katalogos
fi
fi
elif [ "$1" = "-d" -a ( "$3" = "-b" -o "$3" = "-r" ) ]
if [[ $2 = *[!0-9]* ]]
then
echo "Katalogos's string matching lines will be deleted and blank lines will be in their place, assuming that the third argument equals -b, else just the lines will be deleted!"
if [ $(grep -e $2 katalogos | wc -l) -eq 0 ]
then
echo "String is not matched."
else
if [ "$3" = "-b" ]
then
sed -i "$3" katalogos | sed -i '$ a '
echo "A blank line inserted in place of the deleted one."
else
sed -i "$3" katalogos
echo "Line deleted."
fi
fi
fi
elif [ "$1" = "-n" ]
echo "katalogos's number of blank lines will be shown with the ability to delete them!"
grep -cvP '\S' katalogos
read -p "Do you want to delete them? Type 1 for yes or 0 for no... " ans
if [ $ans -eq 1 ]
then
grep -cvP '\S' file | sed -i
echo "Lines deleted."
fi
else
echo "Help centre!"
echo "-Type ./telcat -a to insert a new line to katalogos."
echo "-Type ./telcat -l to see the contents of katalogos sorted numerically (excluding blank lines)."
echo "-Type ./telcat -s plus a number to see the contents of katalogos sorted by the data that the number points to."
echo "-Type ./telcat -c plus a keyword to see only the lines that match with the word given."
echo "-Type ./telcat -d plus a keyword and -b or -r to delete the lines that contain the word given. Specifically if the third argument is -b it will automatically add a blank line to the deleted one and if it is -r it will not."
echo "-Type ./telcat -n to see the number of blank lines of katalogos."
echo "End of help centre!"
fi

Having difficulty writing a script to sort words

I am dealing with sorting words in Bash according to a given argument. I am given either argument -r, -a , -v or -h and according to it there are options to sort the words, as you can see at my "help".
Somehow, if I pass the argument -r it creates an error. I really don't understand what I am doing wrong, as if[["$arg"=="-a"]] works, but I have to use case somehow.
Here is my code:
#!/bin/bash
# Natalie Zubkova , zubkonat
# zubkonat#cvut.fel.cz , LS
#help
help="This script will calculate occurances of words in a given file, and it will sort them according to the given argument in following order> \n
without parametre = increasing order according to a number of occurance\n
-r = decreasing order according to a number of occurance\n
-a = in alphabetical increasing order\n
-a -r = in alphabetical decreasing order\n
There are also special cases of the given parametre, when the script is not sorting but:\n
-h = for obtaining help \n
-v = for obtaining a number of this task "
# this function will divide a given chain into a words, so we can start calculating the occurances, we also convert all the capital letters to the small ones by - tr
a=0;
r=0;
EXT=0;
if [ "$1" == "-h" ]; then
echo $help
exit 0
fi
if [ "$2" == "-h" ]; then
echo $help
exit 0
fi
if [ "$1" == "-v" ]; then
echo "5"
exit 0
fi
if [ "$2" == "-v" ]; then
echo "5"
exit 0
fi
function swap {
while read x y; do
echo "$y" "$x";
done
}
function clearAll {
sed -e 's/[^a-z]/\n/gI' | tr '[A-Z]' '[a-z]' | sort | uniq -c |awk '{i++; if(i!=1) print $2" "$1}' #swap
}
for arg do
case "$arg" in
"-a")
a=1
;;
"-r")
r=1
;;
"-v")
echo "5" #number of task is 5
exit 0
;;
"-h")
echo $help
exit 0
;;
"-?")
echo "invalid parametre, please display a help using argument h"
exit 0
;;
esac
done
#Sort according to parametres -a and -r
function sortWords {
if [[ a -eq 1 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk1
fi
fi
if [[ a -eq 1 ]]; then
if [[ r -eq 1 ]]; then
clearAll | sort -nk1 -r
fi
fi
if [[ r -eq 1 ]]; then
if [[ a -eq 0 ]]; then
clearAll | sort -nk2 -r
fi
fi
if [[ a -eq 0 ]]; then
if [[ r -eq 0 ]]; then
clearAll | sort -nk2
fi
fi
}
#code is from Stackoverflow.com
function cat-all {
while IFS= read -r file
do
if [[ ! -z "$file" ]]; then
cat "$file"
fi
done
}
#histogram
hist=""
for arg do
if [[ ! -e "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -f "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
elif [[ ! -r "$arg" ]]; then
EXT=1;
echo "A FILE DOESNT EXIST" >&2
continue;
fi
done
for arg do
hist="$hist""$arg""\n"
done
echo -e "$hist" | cat-all | sortWords
exit $EXT;
Here is what our upload system which does some test to see if our program works says:
Test #6
> b5.sh -r ./easy.txt
ERROR: script output is wrong:
--- expected output
+++ script stdout
## --- line 1 (167 lines) ; +++ no lines ##
-the 89
-steam 46
-a 39
-of 37
-to 35
...
script written 484 lines, while 484 lines are expected
script error output:
A FILE DOESNT EXIST
cat: invalid option -- 'r'
Try `cat --help' for more information.
script exit value: 1
ERROR: Interrupted due to failed test
If anyone could help me I would really appreciate it.
You forgot to move the parameter index position with shift:
"-r")
r=1
shift
;;
shift above moves to the next command line arg: ./easy.txt in your case.
Without it, read -r file will read -r instead of the file name.

Check if swap space exist in bash

I want to check whether Swap space exist on a centos box in my bash script.Thus in cash there are no swap space on the server , then i can create swap.
I tried this approach
if [[ -n $(swapon -s) ]]; then
:
else
mkswap /dev/vda2 &> /dev/null
swapon /dev/vda2
fi
Obviously it won't work since even when there is no swap swapon -s will return a string
Filename Type Size Used Priority
This works nicely for me:
if free | awk '/^Swap:/ {exit !$2}'; then
echo "Have swap"
else
echo "No swap"
fi
I don't see a means to do this just using 'swapon' since it:
- always returns at least one line
- always returns an error code of '0'
You could 'count lines' and if less then 2 then take the 'else' branch, i.e.
if [[ $(swapon -s | wc -l) -gt 1 ]] ; then echo "OK" ; else echo "Bad" ; fi
OK
if [[ $(swapon -s | wc -l) -gt 2 ]] ; then echo "OK" ; else echo "Bad" ; fi
Bad
Or simply check for 'devices' in the swapon output, i.e.:
if [[ $(swapon -s | grep -ci "/dev" ) -gt 0 ]] ; then echo "OK" ; else echo "Bad" ; fi

Bash split substring

I am receiving numeric variables sometimes with 2, other times with 3 digits, like '321' and '32'. And I want to put a dot among each of the numbers. So if I receive '32', I got to echo '3.2' and if I receive '321' I echo '3.2.1'.
This is what I did:
S='321'
SL="${#S}" #string lentgh
n1=`echo $S | cut -c 1-1`
n2=`echo $S | cut -c 2-2`
if [ "$SL" -eq 2 ]; then
echo $n1.$n2
elif [ "$SL" -eq 3 ]; then
n3=`echo $S | cut -c 3-3`
echo $n1.$n2.$n3
else
die 'Works only with 2 or 3 digits'
fi
My question is: is there any shorter way of doing the same thing?
UPDATE:
Shorter but still verbose:
SL="${#1}" #string lentgh
S=$1
if [ "$1" -eq 3 ]; then
$n3=".${S:2:1}"
fi
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
echo "${S:0:1}.${S:1:1}$n3"
UPDATE 1:
If I include the if block, the sed+regex version will be quite as long as the pure bash version:
SL="${#1}" #string lentgh
S=$1
N=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${N%%.}
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
Or, using a one line sed+regex with two expressions:
SL="${#1}" #string lentgh
echo $1 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'
if [ "$SL" -lt 2 ] && [ "$SL" -gt 3 ]; then
die 'Works only with 2 or 3 digits'
fi
Thanks.
I prefer also the sed for that:
echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2.1
echo 32 | sed -e 's/\([[:digit:]]\)/.\1/g' | cut -b2- -> 3.2
Or without cut it looks like this
echo 321 | sed -e 's/\([[:digit:]]\)/.\1/g' -e 's/^\.//'
Here is one. This will work for any string length.
#!/bin/bash
#s is the string
#fs is the final string
echo "Enter string"
read s
n="${#s}"
fs=""
i=0
for ((i=0; i<n; i++))
do
fs="$fs.${s:i:1}"
done
#find the length of the final string and
#remove the leading '.'
n="${#fs}"
fs="${fs:1}"
echo "$fs"
It's not that pretty, but at least it's short:
num=$(echo $S | sed -r "s/([0-9])/\1./g")
echo ${num%%.}
S='321'
perl -e "print join '.', split //, shift" "$S"

Resources