Bash - How to make bash more efficient and run faster - bash

Is there any way to make the code run faster? i have tried everything!
Basically what i'm trying to do is:
to calculate all combinations of 52 variables, but only show the combinations where all numbers are only listed once! so there can't be fx: two 1's or three 49's
#!/bin/bash
#Enter here how many cards you use
howmanycards=52;
let run=$howmanycards+1;
i=1;
u=52;
totalrun=0;
SECONDS=0;
while [ $i -lt $run ]; do
let card$i=$u;
let i++;
let u--;
done
while [ -lt ]; do
let i=1;
if [ "$card1" -gt "52" ]; then let card2++;let card1=1; fi
if [ "$card2" -gt "52" ]; then let card3++;let card2=1; fi
if [ "$card3" -gt "52" ]; then let card4++;let card3=1; fi
if [ "$card4" -gt "52" ]; then let card5++;let card4=1; fi
if [ "$card5" -gt "52" ]; then let card6++;let card5=1; fi
if [ "$card6" -gt "52" ]; then let card7++;let card6=1; fi
if [ "$card7" -gt "52" ]; then let card8++;let card7=1; fi
if [ "$card8" -gt "52" ]; then let card9++;let card8=1; fi
if [ "$card9" -gt "52" ]; then let card10++;let card9=1; fi
if [ "$card10" -gt "52" ]; then let card11++;let card10=1; fi
if [ "$card11" -gt "52" ]; then let card12++;let card11=1; fi
if [ "$card12" -gt "52" ]; then let card13++;let card12=1; fi
if [ "$card13" -gt "52" ]; then let card14++;let card13=1; fi
if [ "$card14" -gt "52" ]; then let card15++;let card14=1; fi
if [ "$card15" -gt "52" ]; then let card16++;let card15=1; fi
if [ "$card16" -gt "52" ]; then let card17++;let card16=1; fi
if [ "$card17" -gt "52" ]; then let card18++;let card17=1; fi
if [ "$card18" -gt "52" ]; then let card19++;let card18=1; fi
if [ "$card19" -gt "52" ]; then let card20++;let card19=1; fi
if [ "$card20" -gt "52" ]; then let card21++;let card20=1; fi
if [ "$card21" -gt "52" ]; then let card22++;let card21=1; fi
if [ "$card22" -gt "52" ]; then let card23++;let card22=1; fi
if [ "$card23" -gt "52" ]; then let card24++;let card23=1; fi
if [ "$card24" -gt "52" ]; then let card25++;let card24=1; fi
if [ "$card25" -gt "52" ]; then let card26++;let card25=1; fi
if [ "$card26" -gt "52" ]; then let card27++;let card26=1; fi
if [ "$card27" -gt "52" ]; then let card28++;let card27=1; fi
if [ "$card28" -gt "52" ]; then let card29++;let card28=1; fi
if [ "$card30" -gt "52" ]; then let card31++;let card30=1; fi
if [ "$card31" -gt "52" ]; then let card32++;let card31=1; fi
if [ "$card32" -gt "52" ]; then let card33++;let card32=1; fi
if [ "$card33" -gt "52" ]; then let card34++;let card33=1; fi
if [ "$card34" -gt "52" ]; then let card35++;let card34=1; fi
if [ "$card35" -gt "52" ]; then let card36++;let card35=1; fi
if [ "$card36" -gt "52" ]; then let card37++;let card36=1; fi
if [ "$card37" -gt "52" ]; then let card38++;let card37=1; fi
if [ "$card38" -gt "52" ]; then let card39++;let card38=1; fi
if [ "$card39" -gt "52" ]; then let card40++;let card39=1; fi
if [ "$card40" -gt "52" ]; then let card41++;let card40=1; fi
if [ "$card41" -gt "52" ]; then let card42++;let card41=1; fi
if [ "$card42" -gt "52" ]; then let card43++;let card42=1; fi
if [ "$card43" -gt "52" ]; then let card44++;let card43=1; fi
if [ "$card44" -gt "52" ]; then let card45++;let card44=1; fi
if [ "$card45" -gt "52" ]; then let card46++;let card45=1; fi
if [ "$card46" -gt "52" ]; then let card47++;let card46=1; fi
if [ "$card47" -gt "52" ]; then let card48++;let card47=1; fi
if [ "$card48" -gt "52" ]; then let card49++;let card48=1; fi
if [ "$card49" -gt "52" ]; then let card50++;let card49=1; fi
if [ "$card50" -gt "52" ]; then let card51++;let card50=1; fi
if [ "$card51" -gt "52" ]; then let card52++;let card51=1; fi
while [ $i -lt $run ]; do
temp=${card}${i};
if [ "$temp" = "$card1" ] || [ "$temp" = "$card2" ] ||
[ "$temp" = "$card3" ] || [ "$temp" = "$card4" ] ||
[ "$temp" = "$card5" ] || [ "$temp" = "$card6" ] ||
[ "$temp" = "$card7" ] || [ "$temp" = "$card8" ] ||
[ "$temp" = "$card9" ] || [ "$temp" = "$card10" ] ||
[ "$temp" = "$card11" ] || [ "$temp" = "$card12" ] ||
[ "$temp" = "$card13" ] || [ "$temp" = "$card14" ] ||
[ "$temp" = "$card15" ] || [ "$temp" = "$card16" ] ||
[ "$temp" = "$card17" ] || [ "$temp" = "$card18" ] ||
[ "$temp" = "$card19" ] || [ "$temp" = "$card20" ] ||
[ "$temp" = "$card21" ] || [ "$temp" = "$card22" ] ||
[ "$temp" = "$card23" ] || [ "$temp" = "$card24" ] ||
[ "$temp" = "$card25" ] || [ "$temp" = "$card26" ] ||
[ "$temp" = "$card27" ] || [ "$temp" = "$card28" ] ||
[ "$temp" = "$card29" ] || [ "$temp" = "$card30" ] ||
[ "$temp" = "$card31" ] || [ "$temp" = "$card32" ] ||
[ "$temp" = "$card33" ] || [ "$temp" = "$card34" ] ||
[ "$temp" = "$card35" ] || [ "$temp" = "$card36" ] ||
[ "$temp" = "$card37" ] || [ "$temp" = "$card38" ] ||
[ "$temp" = "$card39" ] || [ "$temp" = "$card40" ] ||
[ "$temp" = "$card41" ] || [ "$temp" = "$card42" ] ||
[ "$temp" = "$card43" ] || [ "$temp" = "$card44" ] ||
[ "$temp" = "$card45" ] || [ "$temp" = "$card46" ] ||
[ "$temp" = "$card47" ] || [ "$temp" = "$card48" ] ||
[ "$temp" = "$card49" ] || [ "$temp" = "$card50" ] ||
[ "$temp" = "$card51" ] || [ "$temp" = "$card52" ]; then
let usefull++;
fi
let i++;
done
if [ $usefull -gt 51 ]; then
echo "[loops($totalrun)] $card52-$card51-$card50-$card49-$card48-$card47-$card46-$card45-$card44-$card43-$card42-$card41-$card40-$card39-$card38-$card37-$card36-$card35-$card34-$card33-$card32-$card31-$card30-$card29-$card28-$card27-$card26-$card25-$card24-$card23-$card22-$card21-$card20-$card19-$card18-$card17-$card16-$card15-$card14-$card13-$card12-$card11-$card10-$card9-$card8-$card7-$card6-$card5-$card4-$card3-$card2-$card1";
fi
let usefull=0;
if [ "$card52" -gt "52" ]; then
echo " ";
duration=$SECONDS
echo "$(($duration / 60)) min and $(($duration % 60)) sec";
exit;
fi
let card1++;
let totalrun++;
done
I have tried to create code priorities, but it dosen't seem to make a difference!
but what i have figured out is that the code that make the check, if there more than one number in it, is using a lot of performance! And i don't know what to do about that!
Thank you for your help.

Firstly, I think you need a workable implementation to get combinations in bash. Here is one that works with associative arrays. I implemented it to mimic this Java code:
#!/bin/bash
function combine() {
local index="$1"
local -a prefix=("${!2}")
local -a postfix=("${!3}")
local i=$index
while [ $i -lt ${#prefix[#]} ]; do
postfix+=(${prefix[#]:$i:1})
echo ${postfix[#]}
combine "$((i + 1))" prefix[#] postfix[#]
local postfix_last_char="$((${#postfix[#]} - 1))"
postfix=(${postfix[#]:0:$postfix_last_char})
((i++))
done
}
function get_combinations() {
local -a prefix_table=("${!1}")
local -a postfix_table=()
local index=0
combine 0 prefix_table[#] postfix_table[#]
}
function example_use() {
# Get all combinations of the numbers 0 to 9.
foo=({0..9})
get_combinations foo[#]
}
Now, one can begin to think about modifying the combine() function to impose an additional condition that we will only print out sets (arrays which contain only distinct elements.) So we can replace the line:
echo ${postfix[#]}
By a new call:
echo_if_is_a_set postfix[#]
Which is implemented as:
function echo_if_is_a_set() {
local -a input=("${!1}")
local -a entry_count_lookup
# Tally up the occurrences of each item.
for item in ${input[#]}; do
local current_count=${entry_count_lookup[$item]:-0}
((current_count++))
entry_count_lookup[$item]=$current_count
done
for value in ${!entry_count_lookup[#]}; do
if [ ${entry_count_lookup[$value]} -ne 1 ]; then
return
fi
done
echo ${input[#]}
}
Okay, but not quite done. We might still print the same combination twice, in the event that was passed in repeat elements. For example, if you did:
foo=(1 1 1)
get_combinations foo[#]
So, let's just call uniq, changing get_combinations to:
function get_combinations() {
local -a prefix_table=("${!1}")
local -a postfix_table=()
local index=0
combine 0 prefix_table[#] postfix_table[#] | sort | uniq
}
And, we're done. Running this with the input ({1..3}) prints:
1
1 2
1 2 3
1 3
2
2 3
3
But the nice thing about this implementation is that because it uses the associative arrays, we can actually be general, and input something like (2 Racecar 34), which would return:
2
2 34
2 Racecar
2 Racecar 34
34
Racecar
Racecar 34
Anyway, as a last comment, I'd say start with something like this and then you can optimize the algorithm for efficiency.
Also, Bash is probably not at all the right language for this. :-D

Related

Complicated if-then-else statement

So, I simplified this code. Every time it runs, else or $msg4 is always executed. How do I change it so it only does else if the $nick part doesn't match?
if [ "$who" = "$nick1" ]
then echo $msg1
fi
if [ "$who" = "$nick2" ]
then echo $msg2
fi
if [ "$who" = "$nick3" ]
then echo $msg3
else $msg4
fi
Here you can read how Bash if statements work: https://www.gnu.org/software/bash/manual/html_node/Conditional-Constructs.html#Conditional-Constructs
There you can see there is an elif which you should use to chain multiple if - else things together so that the final else is only executed if none of the if statements match. Result:
if [ "$who" = "$nick1" ]
then
echo $msg1
elif [ "$who" = "$nick2" ]
then
echo $msg2
elif [ "$who" = "$nick3" ]
then
echo $msg3
else
echo $msg4
fi
You can also write the then on the same line as if if you add a ; before then:
if [ "$who" = "$nick1" ]; then
echo $msg1
elif [ "$who" = "$nick2" ]; then
echo $msg2
elif [ "$who" = "$nick3" ]; then
echo $msg3
else
echo $msg4
fi
This is often easier to read.
Use case .. esac
case "$who" in
"$nick1") echo "$msg1";;
"$nick2") echo "$msg2";;
"$nick3") echo "$msg3";;
*) echo "$msg4";;
esac

script for testing if number is in interval

I am trying to test if a number is in the interval [1;100] here is what I did:
var=10
if [ $["$var" -gt "1" ] -a $["$var" -lt "100"] ] ; then
echo "yes"
else
echo "no"
fi
however when I run the script I get the error message:
./yourscript:line 2 10 -gt 1:error syntax in expression ,any ideas why?
delete unnecessaries and use &&:
var=10
if [ $var -gt 1 ] && [ $var -lt 100 ] ; then #or with -a if [ $var -gt 1 -a $var -lt 100 ] ;
echo "yes"
else
echo "no"
fi

bash if-statement check for file

I know how to check for a file in bash using this code
file=$1
if [ -f "$file" ]
then
...
fi
But I want to do something when it's not a file.
file=$3
if [ "$1" == "" ] || [ "$2" == "" ] || [ $file is not a file??? ]
then
echo "use: notEmpty notEmpty file"
fi
Can anyone help me out?
if [ "$1" == "" ] || [ "$2" == "" ] || [ ! -f "$file" ]
The whitespaces after [ and before ] are important.

How do I find the middle of three numbers in shell script

#!/bin/bash
echo "Enter three numbers and this program will give you the middle number : " ; read num1 ; read num2 ; read num3
if [ "$num1" -gt "$num2" ] && [ "$num1" -lt "$num3" ] || [ "$num1" -lt "$num2" ] && [ "$num1" -gt "$num3" ]; then
{
echo "The middle number is $num1"
}
elif [ "$num2" -gt "$num1" ] && [ "$num2" -lt "$num3" ] || [ "$num2" -lt "$num1" ] && [ "$num2" -gt "$num3" ]; then
{
echo "The middle number is $num2"
}
elif [ "$num3" -gt "$num1" ] && [ "$num3" -lt "$num2" ] || [ "$num3 -lt "$num1" ] && [ "$num3" -gt "$num2" ]; then
{ echo "The middle number is $num3" }
fi
The problem I have is with the or condition. I input the numbers 1, 2, and 3, but I get the middle number as 1 all the time.
How about this:
getmid() {
if (( $1 <= $2 )); then
(( $1 >= $3 )) && { echo $1; return; }
(( $2 <= $3 )) && { echo $2; return; }
fi;
if (( $1 >= $2 )); then
(( $1 <= $3 )) && { echo $1; return; }
(( $2 >= $3 )) && { echo $2; return; }
fi;
echo $3;
}
# All permutations of 1, 2 and 3 print 2.
getmid 1 2 3
getmid 2 1 3
getmid 1 3 2
getmid 3 1 2
getmid 2 3 1
getmid 3 2 1
This one should work:
#!/bin/bash
echo "Enter three numbers and this program will give you the middle number : " ; read num1 ; read num2 ; read num3;
if [ "$num1" -gt "$num2" ] && [ "$num1" -lt "$num3" ]; then
{
echo "The middle number is" $num1 ;
}
elif [ "$num1" -lt "$num2" ] && [ "$num1" -gt "$num3" ]; then
{
echo "The middle number is" $num1 ;
}
elif [ "$num2" -gt "$num1" ] && [ "$num2" -lt "$num3" ]; then
{
echo "The middle number is" $num2 ;
}
elif [ "$num2" -lt "$num1" ] && [ "$num2" -gt "$num3" ]; then
{
echo "The middle number is" $num2 ;
}
elif [ "$num3" -gt "$num1" ] && [ "$num3" -lt "$num2" ]; then
{
echo "The middle number is" $num3 ;
}
elif [ "$num3" -lt "$num1" ] && [ "$num3" -gt "$num2" ]; then
{
echo "The middle number is" $num3 ;
}
fi

How to use IF loop in unix for multiple conditions

I need a logic to implement the following logic in unix
if ( $a !="xyz" || $d !="abc" ) && ( $b= $c))
then
echo "YES WORKING"
fi
I tried below code not working
if [ [ [ $a != "xyz" ] -o [ $d != "abc" ] ] -a [ "$b" = "$c" ] ]
then
echo "YES WORKING"
fi
getting error as
:[ :] unexpected operator/operand
You can do something like this:
[ $a != "xyz" -o $d != "abc" ] && [ "$b" = "$c" ] && echo "YES WORKING"
Your logic should work easy in shells supporting [[ ]]:
if [[ ($a != "xyz" || $d != "abc") && $b = "$c" ]]; then
echo "YES WORKING"
fi
Although there's a way for those that doesn't:
if ([ ! "$a" = "xyz" ] || [ ! "$d" = "abc" ]) && [ "$b" = "$c" ]; then
echo "YES WORKING"
fi
But that's still inefficient since you'd be summoning subshells, so use { } but the syntax is a little ugly:
if { [ ! "$a" = "xyz" ] || [ ! "$d" = "abc" ]; } && [ "$b" = "$c" ]; then
echo "YES WORKING"
fi

Resources