I am stuck on a part and i don't understand why, let me paste my code:
local correctId=false
echo $ticketMessage
read deviceId
until [[ $deviceId =~ [0-9]+ && correctId = true ]]; do
if [ ! -e $baseDevicesPath"/$deviceId" ]; then
echo $deviceError
correctId=false
else
correctId=true
fi
if [[ ! $deviceId =~ [0-9]+ ]]; then
echo $ticketMessage
fi
read deviceId
done
echo "I DONT COME HERE?"
if both deviceId and correctId are true, it should exit the until loop and go further? but it doesn't, any idea what i do wrong here?
You just have a simple typo. you are missing the $ in front of correctID in your condition:
local correctId=false
echo $ticketMessage
read deviceId
until [[ $deviceId =~ [0-9]+ && $correctId = true ]]; do
if [ ! -e $baseDevicesPath"/$deviceId" ]; then
echo $deviceError
correctId=false
else
correctId=true
fi
if [[ ! $deviceId =~ [0-9]+ ]]; then
echo $ticketMessage
fi
read deviceId
done
echo "NOW YOU WILL END HERE"
Change correctId into $correctId (of ${correctId}).
I would add double quotes:
local correctId="false"
echo ${ticketMessage}
read deviceId
until [[ $deviceId =~ [0-9]+ && "${correctId}" = "true" ]]; do
if [ ! -e "${baseDevicesPath}/${deviceId}" ]; then
echo ${deviceError}
correctId="false"
else
correctId="true"
fi
if [[ ! "${deviceId}" =~ [0-9]+ ]]; then
echo ${ticketMessage}
fi
read deviceId
done
echo "Do you come here?"
here is a bit more readable solution
function findDevice {
echo $ticketMessage;
read deviceId;
while true; do
local errorMessage;
if [[ $deviceId =~ [0-9]+ ]]; then
if [ -e $baseDevicesPath"/$deviceId" ]; then
#valid input, breaking the loop
break;
fi
errorMessage=$deviceError;
else
errorMessage=$ticketMessage;
fi
echo $errorMessage;
read deviceId;
done
}
Related
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.
I want to match a condition in bash containing "whitespaces" and "OR" condition within strings. I am unable to do as i am new to shell scripting, please help as it going to else loop where it is matching ${myarrlin[1]} . I am getting "Centrify is disabled" but i want the condition to be true here. Here is my code :
FILE_CENT="/etc/nsswitch.conf"
OS=`uname`
if [[ $OS = 'Linux' ]]; then
if [[ -e $FILE_CENT ]]; then
echo "nsswitch.conf found, Proceeding further..."
while read -r LINE
do
if [[ $LINE =~ ^passwd ]]; then
myarrlin=($LINE)
if [[ ${myarrlin[1]} =~ ^(centrify)|(centrifydc)[[:space:]]* || ${myarrlin[1]} =~ [[:space:]]+(centrify)|(centrifydc)[[:space:]]* ]]; then
echo "Centrify is enabled"
else
echo "Centrify is disabled"
fi
fi
done < $FILE_CENT
else
echo "nsswitch.conf does not exist in $OS, cannot fetch CENTRIFY information!"
fi
fi
nsswitch.conf >>>
passwd: centrify files
or
passwd: centrifydc files
or
passwd: files centrify
or
passwd: files centrifydc
Why are you doing this: myarrlin=($LINE) ?
If you just want to know if the line contains centrify:
while read -r LINE
do
if [[ ${LINE} =~ ^passwd ]]; then
if [[ ${LINE} == *"centrify"* ]]; then
echo "Centrify is enabled"
else
echo "Centrify is disabled"
fi
fi
done < $FILE_CENT
I am using if statement with multiple condition in bash.
How can I reduce the following line syntax. So that it looks good from design point of you.
if [ "$1" != "-l" ] && [ "$1" != "-a" ] && [ "$1" != "-h" ] && [ "$1" != "" ] && [ "$1" = "-d" ] || [ "$1" = "-mv" ] || [ "$1" = "-dv" ] || [ "$1" = "-mr" ] || [ "$1" = "-dr" ];
Thanks
Use pattern matching.
if [[ $1 && $1 != -[lah] && $1 == -#(d|mv|dv|mr|dr) ]]; then
#(...) is an example of an extended pattern, which should be recognized by default inside [[ ... ]] in recent versions of bash. If you version is not so recent, add shopt -s extglob to the beginning of your script.
In fact, you can drop the $1 && $1 != -[lah] because its truth would be implied by the truth of $1 == -#(...).
if [[ $1 == -#(d|mv|dv|mr|dr) ]]; then
You could also just use a POSIX-compliant case statement:
case $1 of
-d|-mv|-dv|-mr|-dr) echo good option ;;
*) echo bad option ;;
esac
You can create 2 arrays for matching and non matching values and check if element $1 matches any element in the array or not like below.
nonmatch_array=( "-l" "-a" "-h" "" )
match_array=( "-d" "-mv" "-dv" "-mr" "-dr" )
if [ `echo ${match_array[#]} | grep "$1"` ] || ! [ `echo ${nonmatch_array[#]} | grep "$1"` ] ; then
echo "is in array"
else
echo "is not in array"
fi
Hope it should work for you.
First try to limit the length of the code on 1 line.
if [ [ "$1" != "-l" ]
&& [ "$1" != "-a" ]
&& [ "$1" != "-h" ]
&& [ -n "$1" ]
&& ( [ "$1" = "-d" ]
|| [ "$1" = "-mv" ]
|| [ "$1" = "-dv" ]
|| [ "$1" = "-mr" ]
|| [ "$1" = "-dr" ] ) ];
I added braces, to make clear what you mean with the or's.
Now you can combine all matches with a regular expression:
if [[ ! ("$a" =~ ^-(l|a|h|d|)$)
&& "$a" =~ ^-(mv|dv|mr|dr)$ ]]; then
echo "Yes $a matches"
fi
but reconsider what you are testing. The test will only be true when it matches -mv/-dv/-mr/-dr, so you do not need to test for the options lah.
if [[ "$a" =~ ^-(d|mv|dv|mr|dr)$ ]]; then
echo "Yes $a matches"
fi
You can use a variable for extracting the options:
options="d|mv|dv|mr|dr"
if [[ "$a" =~ ^-(${options})$ ]]; then
echo "Yes $a matches"
fi
Everytime the code is becoming hard to read (and also for long code or repeating statements), you should consider using a function.
The next function is short, but hard to read:
options="d|mv|dv|mr|dr"
function checkoption1 {
[[ "$a" =~ ^-(${options})$ ]]
}
checkoption1 "$a" &&
echo "Yes $a matches"
I would choose for a slightly more verbose function. I will include your original tests for lah for showing the possibilities.
# checkoption return 0 for match,
# returns 1 for forbidden option
# returns 2 for undefined option
function checkoption2 {
case "$1" in
-d|-mv|-dv|-mr|-dr) return 0 ;;
-l|-a|-h|"") return 1;;
*) return 2;;
esac
}
checkoption2 "$a" &&
echo "Yes $a matches"
You should make some testruns before accepting your code.
I have made some tests with a small loop (now all answers together)
function checkoption1 {
[[ "$a" =~ ^-(${options})$ ]]
}
# checkoption return 0 for match,
# returns 1 for forbidden option
# returns 2 for undefined option
function checkoption2 {
case "$1" in
-d|-mv|-dv|-mr|-dr) return 0 ;;
-l|-a|-h|"") return 1;;
*) return 2;;
esac
}
for a in -mv mv -mvx -ms -mr -dr; do
if [[ ! ("$a" =~ ^-(l|a|h|)$)
&& "$a" =~ ^-(d|mv|dv|mr|dr)$ ]]; then
echo "Yes $a matches"
fi
if [[ "$a" =~ ^-(d|mv|dv|mr|dr)$ ]]; then
echo "Yes $a matches"
fi
options="d|mv|dv|mr|dr"
if [[ "$a" =~ ^-(${options})$ ]]; then
echo "Yes $a matches"
fi
checkoption1 "$a" &&
echo "Yes $a matches"
checkoption2 "$a" &&
echo "Yes $a matches 2"
done
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
This question already has answers here:
How to check if a string contains a substring in Bash
(29 answers)
Closed 9 years ago.
if[ xxx ]
how to expresss the string or file include '.'
I am new to study shell,thanks for any help
You can use the matching operator:
$ if [[ "abc.def" =~ \. ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ "abcdef" =~ \. ]]; then echo "yes"; else echo "no"; fi
no
This matches if the dot is the first or last (or only) character in the string. If you expect characters on both sides of the dot, you can do the following:
$ if [[ "ab.cdef" =~ .\.. ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ ".abcdef" =~ .\.. ]]; then echo "yes"; else echo "no"; fi
no
$ if [[ "abcdef." =~ .\.. ]]; then echo "yes"; else echo "no"; fi
no
You can also use pattern matching:
$ if [[ "ab.cdef" == *?.?* ]]; then echo "yes"; else echo "no"; fi
yes
$ if [[ ".abcdef" == *?.?* ]]; then echo "yes"; else echo "no"; fi
no
$ if [[ "abcdef." == *?.?* ]]; then echo "yes"; else echo "no"; fi
no
A good reference for both patterns and regexes is at Greg's Wiki
bash supports glob-style pattern matching:
if [[ "$file" = *?.?* ]]; then
...
fi
Note that this assumes a prefix as well - this also ensures that it will not match the . and .. directories.
If you want to check for a specific extension:
if [[ "$file" = *?.foo ]]; then
...
fi
echo "xxx.yyy" | grep -q '\.'
if [ $? = 0 ] ; then
# do stuff
fi
Or
echo "xxx.yyy" | grep -q '\.' && <one statement here>
#e.g.
echo "xxx.yyy" | grep -q '\.' && echo "got a dot"