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
Related
This question already has answers here:
Compare/Difference of two arrays in Bash
(9 answers)
Closed 2 years ago.
firstvar="PRIMARY"
secondvar="SECONDARY"
thirdvar="TERTIARY"
array=($firstvar $secondvar $thirdvar)
echo ${array[*]} //prints PRIMARY SECONDARY TERTIARY
I want to iterate over this array in my bash script, and check if the 3 elements (PRIMARY SECONDARY TERTIARY) are present in the array with no specific order.
If yes, echo "Success. If not, echo Failed. What would be a good way to approach this?
EDIT:
I forgot to mention that if an element(e.g. PRIMARY) is missing from the array, it should also print out Primary is missing.
For a similar array:
array_health=($firsthealth $secondhealth $thirdhealth)
These 3 variables can have either 1 or something else
I want to check whether these 3 variables in the array have value=1, how would I check that? They're not initialized as firsthealth=0 or firsthealth=1.
Try bash rematch
[[ ${array[#]} =~ $firstvar ]] && [[ ${array[#]} =~ $secondvar ]] && [[ ${array[#]} =~ $thirdvar ]] && echo ok || echo ko
Then like this
for item in $firstvar $secondvar $thirdvar; {
[[ ${array[#]} =~ $item ]] && echo "$item" || echo "$item is missing"
}
beginner friendly simple loop with simple case counting all vars
followed by test concatenation and simple if statement
array=("$firstvar" "$secondvar" "$thirdvar")
for i in "${array[#]}"
do
echo "$i"
case "$i" in
"$firstvar")
first=$((first+1))
;;
"$secondvar")
second=$((second+1))
;;
"$thirdvar")
third=$((third+1))
;;
esac
done
if [ "$first" ] && [ "$second" ] && [ "$third" ]
then
echo "Success."
else
[ -z "$first" ] && echo "'$firstvar' missing"
[ -z "$second" ] && echo "'$secondvar' missing"
[ -z "$third" ] && echo "'$thirdvar' missing"
fi
regarding your second array basically same, quote your vars
(although it's unclear to me how you want preserve labels as you only have values in array)
array_health=("$firsthealth" "$secondhealth" "$thirdhealth")
uninitialized vars can be test'ed with -z (as above) or have default value with string manipulation
[ -z "$i" ] && echo "${i:-0}"
You can loop over the elements one by one:
flag=0
for ele in "$firstvar" "$secondvar" "$thirdvar"; do
if [[ ! " ${array[#]} " =~ " ${ele} " ]]; then
echo "$ele" is missing
flag=1
fi
done
if [[ flag -eq 0 ]]; then
echo "all there"
fi
If there is a possibility that your elements may have spaces, that will make the test above unreliable. You can use a different delimiter by using printf to add left and right delimiters to characters unlikely to be in your strings:
firstvar="PRIMARY SECONDARY"
secondvar="SECONDARY"
thirdvar="TERTIARY"
array=("$firstvar" "$thirdvar")
flag=0
printf -v tgt "|%s|" "${array[#]}" # |ele1||ele2||eleN|
for ele in "$firstvar" "$secondvar" "$thirdvar"; do
if [[ ! "$tgt" =~ "|${ele}|" ]]; then
echo "$ele" is missing
flag=1
fi
done
if [[ flag -eq 0 ]]; then
echo "all there"
fi
Prints SECONDARY is missing
PROMPT_COMAND runs slow. Code optimization needed.
I use bash builtin PROMPT_COMMAND to customize PS1. Too much echo | grep commands were executed to get colorful git branch indication.The prompt runs much slower. Any ideas about optimazation?
function git_branch {
branch="`git branch 2>/dev/null | grep "^\*" | sed -e "s/^\*\ //"`"
if [ "${branch}" != "" ];then
if [ "${branch}" = "(no branch)" ];then
branch="(`git rev-parse --short HEAD`...)"
fi
echo "$branch"
fi
}
# display last two dentry
function get_curdir {
dir=$(pwd);
last_dirent=${dir##*/}
dir=${dir%/*}
lastbutone_dirent=${dir##*/}
echo -n "${lastbutone_dirent}/${last_dirent}"
}
# colours
RED="\[\033[1;31m\]"
GREEN="\[\033[1;32m\]"
BROWN="\[\033[0;33m\]"
BLUE="\[\033[1;34m\]"
PURPLE="\[\033[1;35m\]"
CYAN="\[\033[1;36m\]"
GRAY="\[\033[0;37m\]"
LIGHT_RED="\[\033[0;31m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_BLUE="\[\033[0;34m\]"
YELLOW="\[\033[1;33m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_GRAY="\[\033[0;37m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour
function prompt_command {
RET=$?
PS1="${CYAN}[${RESTORE}${GREEN}\u${RESTORE} "
PS1+="${BLUE}$(get_curdir)${RESTORE}${CYAN}]${RESTORE}"
BRANCH=$(git_branch)
if [ "${BRANCH}" != "" ]; then
STATUS="$(git status -s 2>/dev/null)"
if echo "${STATUS}" | grep -e "??"; then
PARENTHESES_COLOR=${RED}
else
PARENTHESES_COLOR=${GREEN}
fi >/dev/null
if [ "${STATUS}" = "" ]; then
BRANCH_COLOR=${GREEN}
else
if echo "${STATUS}" | grep -e "^[ M]M"; then
if echo "${STATUS}" | grep -e "^M[ M]"; then
BRANCH_COLOR=${PURPLE}
else
BRANCH_COLOR=${RED}
fi
elif echo "${STATUS}" | grep -e "^M "; then
BRANCH_COLOR=${YELLOW}
fi
fi >/dev/null
PS1+="${PARENTHESES_COLOR}(${RESTORE}${BRANCH_COLOR}$(git_branch)${RESTORE}${PARENTHESES_COLOR})${RESTORE}"
fi
if [ "$RET" = "0" ]; then
PS1+=" ${CYAN}->${RESTORE} "
else
PS1+=" ${RED}->${RESTORE} "
fi
}
export PROMPT_COMMAND=prompt_command
update
I run a simple test in some dir where a git repo resides and use time to get time cost.
for ((i=0;i<10000;i++));do
prompt_command;
done
Performance of the original version above is:
real 3m4.567s
user 1m32.698s
sys 3m2.495s
Then I change [] to [[ ]], diff like this:
17,18c17,18
< if [ "${branch}" != "" ];then
< if [ "${branch}" = "(no branch)" ];then
---
> if [[ "${branch}" != "" ]];then
> if [[ "${branch}" == "(no branch)" ]];then
58c58
< if [ "${BRANCH}" != "" ]; then
---
> if [[ "${BRANCH}" != "" ]]; then
65c65
< if [ "${STATUS}" = "" ]; then
---
> if [[ "${STATUS}" == "" ]]; then
81c81
< if [ "$RET" = "0" ]; then
---
> if [[ "$RET" == "0" ]]; then
Performance get a little worse
real 3m7.690s
user 1m30.717s
sys 3m6.676s
So, [] doesn't matter.
But builtin regex helps a lot.
When I changes to the following and replace $(pwd) with $PWD
if [ "${BRANCH}" != "" ]; then
regex_untracked=".*^\?\?.*"
regex_staged=".*^M[ M].*"
regex_modified=".*^[ M]M.*"
STATUS="$(git status -s 2>/dev/null)"
if [[ ${STATUS} =~ $regex_untracked ]]; then
PARENTHESES_COLOR=${RED}
else
PARENTHESES_COLOR=${GREEN}
fi >/dev/null
if [[ ${STATUS} =~ $regex_modified ]]; then
if [[ ${STATUS} =~ $regex_staged ]]; then
BRANCH_COLOR=${PURPLE}
else
BRANCH_COLOR=${RED}
fi
elif [[ ${STATUS} =~ $regex_staged ]]; then
BRANCH_COLOR=${YELLOW}
else
BRANCH_COLOR=${GREEN}
fi >/dev/null
PS1+="${PARENTHESES_COLOR}(${RESTORE}${BRANCH_COLOR}$(git_branch)${RESTORE}${PARENTHESES_COLOR})${RESTORE}"
fi
Time consumed decreases:
real 2m15.534s
user 1m1.036s
sys 2m15.043s
By the way, without this colorful branch feature, performance is
real 1m0.478s
user 0m29.499s
sys 1m1.411s
bash has its own built-in regular expression matching. For example, replace
echo "${STATUS}" | grep -e "^[ M]M"
with
regex="^[ M]M"
[[ $STATUS =~ $regex ]]
I was writing a bash script to check whether the first word on each line
is equal to a certain value but it isn't returning the expected values.
the bash script
#!/bin/bash
if [ $# != 3 ]; then
echo "Error no file specified, default files will be considered"
a="input.txt"
b="correct.txt"
c="incorrect.txt"
while read -r line; do
d=( $line )
e=${d[0]}
if [ $e != "add" ] || [ $e != "sub" ] || [ $e != "addi" ] || [ $e != "lw" ] || [ $e != "sw" ]
then
echo "error"
else
echo "correct"
fi
done < "$a"
fi
the input.txt file:
ok lol no
right back go
why no right
sub send bye
The actual result is:
error
error
error
error
the expected result is:
error
error
error
correct
try this:
if ! [[ "$e" =~ (add|sub|addi|lw|sw)$ ]];then
complete code:
#!/bin/bash
if [ $# != 3 ]; then
echo "Error no file specified, default files will be considered"
a="input.txt"
b="correct.txt"
c="incorrect.txt"
while read -r line; do
d=( $line )
e=${d[0]}
if ! [[ "$e" =~ (add|sub|addi|lw|sw)$ ]];then
echo "e[$e] - error"
else
echo "e[$e] - correct"
fi
done < "$a"
fi
output:
> ./testInput.bash
Error no file specified, default files will be considered
e[ok] - error
e[right] - error
e[why] - error
e[sub] - correct
A case statement would be clearer.
case $e in
add|sub|addi|lw|sw) echo "correct" ;;
*) echo "error"
esac
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
}
In the following code I want to compare the command line arguments with the parameters but I am not sure what is the current syntax to compare the arguments with parameters..i.e "==" or "-eq".
#!/bin/bash
argLength=$#
#echo "arg = $1"
if [ argLength==0 ]; then
#Running for the very first
#Get the connected device ids and save it in an array
N=0
CONNECTED_DEVICES=$(adb devices | grep -o '\b[A-Za-z0-9]\{8,\}\b'|sed -n '2,$p')
NO_OF_DEVICES=$(echo "$CONNECTED_DEVICES" | wc -l)
for CONNECTED_DEVICE in $CONNECTED_DEVICES ; do
DEVICE_IDS[$N]="$CONNECTED_DEVICE"
echo "DEVICE_IDS[$N]= $CONNECTED_DEVICE"
let "N= $N + 1"
done
for SEND_DEVICE_ID in ${DEVICE_IDS[#]} ; do
callCloneBuildInstall $SEND_DEVICE_ID
done
elif [ "$1" -eq -b ]; then
if [ $5 -eq pass ]; then
DEVICE_ID=$3
./MonkeyTests.sh -d $DEVICE_ID
else
sleep 1h
callCloneBuildInstall $SEND_DEVICE_ID
fi
elif [ "$1" -eq -m ]; then
echo "Check for CloneBuildInstall"
if [ "$5" -eq pass ]; then
DEVICE_ID=$3
callCloneBuildInstall $SEND_DEVICE_ID
else
echo "call CloneBuildInstall"
# Zip log file and save it with deviceId
callCloneBuildInstall $SEND_DEVICE_ID
fi
fi
function callCloneBuildInstall {
./CloneBuildInstall.sh -d $SEND_DEVICE_ID
}
From help test:
[...]
STRING1 = STRING2
True if the strings are equal.
[...]
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
But in any case, each part of the condition is a separate argument to [.
if [ "$arg" -eq 0 ]; then
if [ "$arg" = 0 ]; then
Why not use something like
if [ "$#" -ne 0 ]; then # number of args should not be zero
echo "USAGE: "
fi
When/how to use “==” or “-eq” operator in test?
To put it simply use == when doing lexical comparisons a.k.a string comparisons but use -eq when having numerical comparisons.
Other forms of -eq (equal) are -ne (not equal), -gt (greater than), -ge (greater than or equal), -lt (lesser than), and -le (lesser than or equal).
Some may also suggest preferring (( )).
Examples:
[[ $string == "something else" ]]
[[ $string != "something else" ]] # (negated)
[[ $num -eq 1 ]]
[[ $num -ge 2 ]]
(( $num == 1 ))
(( $num >= 1 ))
And always use [[ ]] over [ ] when you're in Bash since the former skips unnecessary expansions not related to conditional expressions like word splitting and pathname expansion.