Adding exceptions during cut returns unwanted results - linux

I have a file that is being generated (sort of an audit file) with who have accessed said file. Looks as follows:
I need to write an alarming system that enters said report and checks for all users. however bash for some reason interprets the "------" as an input.
for i in $(cut -c 8-13 report_file.csv)
do
if [[ $i -eq 'suser' ]] || [[ $i -eq '--------' ]] || [[ $i -eq 'login' ]] || $i -eq 'root']]
then
break
else
echo "email text"+ $i | mailx -s "email subject" $EMAILS_LIST
done
the output for this is:
./script_name.sh: line 26: [[: --------: syntax error: operand
expected (error token is "-")
So as I understand it takes the exception "------" and still sees it as sort of input.
So, what am I missing?

-eq in test (same in extended test [[...]]) is an operator for integers. '---------' is not an integer. Use = to compare strings.
... [[ "$i" = 'suser' ]] || [[ "$i" = '--------' ]] || [[ "$i" = 'login' ]] || [[ "$i" = 'root' ]]
or simpler
... [[ "$i" = 'suser' || "$i" = '--------' || "$i" = 'login' || "$i" = 'root' ]]
or simpler:
case "$i" in
suser|--------|login|root) ;;
*) echo "email text"+ $i | mailx -s "email subject" $EMAILS_LIST; ;;
esac
Side note:
Reading lines from file using for i in $(...) is bad. It's better to use while read -r line; do .... done < <(cut -c 8-13 report_file.csv) or cut -c 8-13 report_file.csv | while read -r line; do ... done see here.

Related

shell script to print only alpha numeric string and ignore all integers

I am novice to linux scripting. For the below example, i need to split the string as per "-" and store the output in an array as a separate element.
Later, i need to validate each element in an array if its an integer or alphanumeric. if its integer, i need to ignore that element and print only non-integer elements. The following script which i am trying is not giving expected output which should be like 'grub2-systemd-sleep-plugin'.
item = grub2-systemd-sleep-plugin-2.02-153.1
IFS='-'
read -rasplitIFS<<< "$item"
for word in "${splitIFS[#]}"; do echo $word; done
Taking a stab at this here...
Depends on how your numbers may be defined, but I believe you could use something like this to removing numbers from the output. I'm not sure if there is a more efficient way to achieve this
for word in ${splitIFS[#]}
do
c=$(echo $word | grep -c -E "^[0-9]+\.{0,}[0-9]+$")
[ $c -eq 0 ] && echo $word
done
If you're using bash, it will be faster if you use built-in tools rather than subshells.
line=grub2-systemd-sleep-plugin-2.02-153.1-foo-1
while [[ -n "$line" ]]
do if [[ "$line" =~ ^[0-9.]+- ]]
then line="${line#*-}"
elif [[ "$line" =~ ^[0-9.]+$ ]]
then break
elif [[ "$line" =~ ^([[:alnum:]]+)- ]]
then echo "${BASH_REMATCH[1]}";
line="${line#*-}"
elif [[ "$line" =~ ^([[:alnum:]]+)$ ]]
then echo "${BASH_REMATCH[1]}";
break
else echo "How did I get here?"
fi
done
or if you prefer,
shopt -s extglob
line=grub2-systemd-sleep-plugin-2.02-153.1-foo-1
while [[ -n "$line" ]]
do case "$line" in
+([0-9.])-*) line="${line#*-}" ;;
+([0-9.])) break ;;
+([[:alnum:]])-*) echo "${line%%-*}"
line="${line#*-}" ;;
+([[:alnum:]])) echo "$line"
break ;;
*) echo "How did I get here?" ;;
esac
done

Check if user exists and belong to group

I want to check three conditions before I run the command: If string (username) match to regexp, if user exists and if user belongs to certain group:
PASSWORD=`/usr/bin/pwgen -sync -1 15`
CHPASSWD=/usr/sbin/chpasswd
FILE=/tmp/password
USER_GROUP=somegroup
while true
do
echo -ne "username: "; read USER_NAME
if [[ "$USER_NAME" =~ ^[a-z_][a-z0-9_]*[$]?$ ]] &&
[[ `getent group $USER_GROUP | grep -o $USER_NAME` -eq 0 ]] &&
[[ `getent passwd $USER_NAME | cut -d ':' -f -1` -eq 0 ]]
then
break
else
echo "error"
fi
done
echo $USER_NAME:$PASSWORD > $FILE
sudo $CHPASSWD < $FILE
rm -f $FILE
The above code works partially only. It checks for regexp and group but it is trying to change password for even if user doesn't exist.
The right solution is:
while true
do
echo -ne "username: "; read USER_NAME
if [[ "$USER_NAME" =~ ^[a-z_][a-z0-9_]*[$]?$ ]]
then
if [[ `getent group $USER_GROUP | grep -w -c "$USER_NAME"` -eq 1 ]]
then
break
else
echo "User not in the right group"
fi
else
echo "Invalid characters"
fi
done

Shell script if condition not evaluated for a help info display for the user

I had written a help menu for reference about the usage of a shell script my_script.sh
echo $'\n\n'
echo $(printf '=%.0s' {1..100})
printf ' %.0s' {1..40}
echo "Welcome"
echo $(printf '=%.0s' {1..100})
echo $'\n'
arg=$1
echo "Input : $arg"
echo
if [[ arg -eq "-h" ]] || [[ arg -eq "-H" ]] || [[ arg -eq "-help" ]] || [[ arg -eq "-Help" ]] || [[ arg -eq "--h" ]] || [[ arg -eq "--H" ]] || [[ arg -eq "--help" ]] || [[ arg -eq "--Help" ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
When I execute `myscript.sh -h' (or any of the '-' prefixed option), it goes to the if condition, but any other argument doesn't. What am I doing wrong here? I'm new to bash scripts.
Two simple problems with your if:
-eq is for integer comparison, = or == for strings
Use $arg in your if (instead of arg).
But: I would recommend using getopts instead of string comparison. This would make the part more robust, taking care of different ordering of parameters, or when one letter parameters are combined into a single argument.
Unfortunately I do not know the exact reason why your code does not work, but I can offer you a quick fix: You can write "==" instead of "-eq" and prefix your variable "arg" with a dollar sign. Then your script should work fine.
Working example (GNU bash 4.4.19):
arg=$1
if [[ $arg == "-h" ]] || [[ $arg == "-H" ]]; then
echo "Help!"
else
echo "Stop!"
fi
the -eq operation is only used for comparing numbers.
To compare strings uses the operation =
You forgot $ sign for variables arg in Bash, should be $arg
When we use variables in Bash, we should better use double quote.
use #() for multiple strings comparison.
so the if conditions [[ arg -eq "-h" ]] should be [[ "$arg" = "-h" ]]
When comparing a variable with multiple strings, we can use [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]].
if [[ "$arg" = #(-h|-H|--help|--HELP|--h|--H|-help|--HELP) ]]; then
echo "Help menu requested...."
echo $'\n\n'
echo $(printf '~%.0s' {1..100})
printf ' %.0s' {1..43}
echo "Help Menu"
echo $(printf '~%.0s' {1..100})
echo $'\n'
exit 0
else
echo "Executing a program...."
./another_script.sh
fi
In addition, we can use boxes(boxes - Command line ASCII boxes unlimited!
) to generate a comment box
cat <<EOF | boxes -a c -d shell -p a5 -s 30x9
HELP MENU
bla bla
EOF
output:
########################################
# #
# #
# HELP MENU #
# #
# bla bla #
# #
# #
########################################

Linux bash if - elif don't work as expected

My Bash-Script has to do something different, according of the beginning of a Variable.
When I run the following script:
#!/bin/bash
line="__B something"
if [ -n $(echo "$line" | grep "^__A") ]; then
echo "Line __A"
elif [ -n $(echo "$line" | grep "^__B") ]; then
echo "Line __B"
elif [ -n $(echo "$line" | grep "^__C") ]; then
echo "Line __C"
elif [ -n $(echo "$line" | grep "^__D") ]; then
echo "Line __D"
elif [ -n $(echo "$line" | grep "^__E") ]; then
echo "Line __E"
elif [ $(echo "$line" | grep "^__F") ]; then
echo -n "Line __F"
else
echo "something other"
fi
Bash does not recognize that the String start's with __B.:
Output is:
Line __A
What's wrong with my Script?
Thank you for your help!
If you run your script using the xtrace option than you can see what is happening:
bash -x your-script
+ line='__B something'
++ echo '__B something'
++ grep '^__A'
+ '[' -n ']'
+ echo 'Line __A'
Line __A
Because you use the old test built-in (otherwise known as [) the expansion is done but results in nothing to test except the -n. Using the [[ keyword will quote things correctly:
bash -x your-script
+ line='__B something'
++ echo '__B something'
++ grep '^__A'
+ [[ -n '' ]]
++ echo '__B something'
++ grep '^__B'
+ [[ -n __B something ]]
+ echo 'Line __B'
Line __B
However, using the external program grep is a waste. Recent versions of bash have regular expressions (REs) built-in (using the binding operator =~) but you don't need REs in this case, simple globbing will do:
#!/bin/bash
line="__B something"
if [[ $line == __A* ]]; then
echo "Line __A"
elif [[ $line == __B* ]]; then
echo "Line __B"
elif [[ $line == __C* ]]; then
echo "Line __C"
elif [[ $line == __D* ]]; then
echo "Line __D"
elif [[ $line == __E* ]]; then
echo "Line __E"
elif [[ $line == __F* ]]; then
echo -n "Line __F"
else
echo "something other"
fi
I believe it has to do with the subtleties of the difference between using [[ and [ in bash if conditionals.
You can either use [[ and ]] everywhere (seems to work), e.g.
if [[ -n $(echo "$line" | grep "^__A") ]]; then
echo "Line __A"
or you can quote the subshell like this
if [ -n "$(echo '$line' | grep '^__A')" ]; then
echo "Line __A"
[[ has fewer surprises and is generally safer to use. But it is not
portable - Posix doesn't specify what it does and only some shells
support it (beside bash, i heard ksh supports it too). For example,
you can do
[[ -e $b ]]
to test whether a file exists. But with [, you have to quote $b,
because it splits the argument and expands things like "a*" (where [[
takes it literally). That has also to do with how [ can be an external
program and receives its argument just normally like every other
program (although it can also be a builtin, but then it still has not
this special handling).
according to this answer here on stackoverflow. So in your case, most likely bash is separating your string on a space and that is having some side effects.

checking if a string is a palindrome

I am trying to check if a string is a palindrome in bash. Here is what I came up with:
#!/bin/bash
read -p "Enter a string: " string
if [[ $string|rev == $string ]]; then
echo "Palindrome"
fi
Now, echo $string|rev gives reversed string. My logic was to use it in the condition for if. That did not work out so well.
So, how can I store the "returned value" from rev into a variable? or use it directly in a condition?
Another variation without echo and unnecessary quoting within [[ ... ]]:
#!/bin/bash
read -p "Enter a string: " string
if [[ $(rev <<< "$string") == "$string" ]]; then
echo Palindrome
fi
A bash-only implementation:
is_palindrome () {
local word=$1
local len=$((${#word} - 1))
local i
for ((i=0; i <= (len/2); i++)); do
[[ ${word:i:1} == ${word:len-i:1} ]] || return 1
done
return 0
}
for word in hello kayak; do
if is_palindrome $word; then
echo $word is a palindrome
else
echo $word is NOT a palindrome
fi
done
Inspired by gniourf_gniourf:
is_palindrome() {
(( ${#1} <= 1 )) && return 0
[[ ${1:0:1} != ${1: -1} ]] && return 1
is_palindrome ${1:1: 1}
}
I bet the performance of this truly recursive call really sucks.
Use $(command substitution):
#!/bin/bash
read -p "Enter a string: " string
if [[ "$(echo "$string" | rev)" == "$string" ]]; then
echo "Palindrome"
fi
Maybe it is not the best implementation, but if you need something with pure sh
#!/bin/sh
#get character <str> <num_of_char>. Please, remember that indexing is from 1
get_character() {
echo "$1" | cut -c "$2"
}
for i in $(seq $((${#1} / 2))); do
if [ "$(get_character "$1" "$i")" != "$(get_character "$1" $((${#1} - i + 1)))" ]; then
echo "NO"
exit 0
fi
done
echo "YES"
and canonical way with bash as well
for i in $(seq 0 $((${#1} / 2 - 1))); do
if [ "${1:$i:1}" != "${1:$((${#1} - i - 1)):1}" ]; then
echo "NO"
exit 0
fi
done
echo "YES"
Skipping all punctuation marks and letter case.
input:He lived as a devil, eh?
output:Palindrome
input:Madam, I am Adam.
output:Not Palindrome
#!/bin/bash
#set -x
read -p "Enter a sentence" message
message=$(echo "$message" | \
sed -e '
s/[[:space:]]//g
s/[[:punct:]]//g
s/\!//g
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
' )
i=0
while read -n 1 letter
do
tempArray[i]="$letter"
((i++))
done < <(echo "$message")
i=0
counter=$((${#message}-1))
while [ "$i" -ne $((${#message}/2)) ]
do
if [ "${tempArray[$i]}" = "${tempArray[$counter]}" ]
then
((i++))
((counter--))
else echo -n "Not ";break
fi
done
echo "Palindrome"
exit

Resources