How to draw below Star Pattern in Shell Script? - linux

I am trying to create below pattern design in Shell script, i have written below codes for the same but i am not getting expected results. can someone help on this?
Desired output-->
*
**
***
****
*****
#! /bin/sh
for ((i=1; i<=5; i++))
do
for ((j=1; j<=5; j++))
do
if [ $j > 6-$i ]
then
echo -n "* "
elif [ $j == 6-$i ]
then
echo -n "* "
else
echo -n " "
fi
done
echo
done
Current output-->
*****
*****
*****
*****
*****

You will need to use another loop to print the pre-pending spaces.
for ((i=1;i<=5;i++)); # Loop to create 5 lines of text
do
for((k=1;k<=(5-i);k++)); # Loop to padd prepending spaces
do
printf "%s" " ";
done;
for ((j=1;j<=i;j++)); # Loop to create asterix
do
printf "%s" "*";
done;
printf "\n"; # Print the carriage return
done

to make the minimum number of changes to your code to get it working; you need to do number comparison, and number addition in order to get your check statements to work:
for ((i=1; i<=5; i++)) ; do
for ((j=1; j<=5; j++)) ; do
if [ $j -ge $((6-$i)) ] ; then
echo -n "* "
else
echo -n " "
fi
done
echo
done
new output:
*
* *
* * *
* * * *
* * * * *

It doesn't need so much arithmetic, you can use printf to add the padding:
n=5
line=''
char='*'
for ((i=1; i<=n; i++)); do
line+=$char
printf "%*s\n" $n "$line"
done
If you want the triangle leaning the other way:
printf "%-*s\n" $n "$line"

Related

Show progress while sleep in bash

I wrote a simple script which must show progress while user waiting. But I get infinitive loop and seems sleep not working. What wrong in this code?
#!/bin/bash
spinner=(
"Working "
"Working. "
"Working.. "
"Working... "
"Working...."
)
while sleep 10
do
for item in ${spinner[*]}
do
echo -en "\r$item"
sleep .1
echo -en "\r \r"
done
done
One idea:
using the bash (system) variable SECONDS to measure our 10 seconds
using a tput code for ovewriting a line
eliminating the spinner[] array (since the only difference in values is the number of trailing periods)
EraseToEOL=$(tput el)
max=$((SECONDS + 10)) # add 10 seconds to current count
while [ $SECONDS -le ${max} ]
do
msg='Waiting'
for i in {1..5}
do
printf "%s" "${msg}"
msg='.'
sleep .1
done
printf "\r${EraseToEOL}"
done
printf "\n"
A small change to OP's current code using the max/SECONDS approach:
spinner=(
"Working "
"Working. "
"Working.. "
"Working... "
"Working...."
)
max=$((SECONDS + 10))
while [[ ${SECONDS} -le ${max} ]]
do
for item in ${spinner[*]}
do
echo -en "\r$item"
sleep .1
echo -en "\r \r"
done
done
Use the in/decrement variable i to put out the array...
#!/bin/bash
countdown(){
spinner=(
"Working "
"Working. "
"Working.. "
"Working... "
"Working...."
)
i=4
if [ ${i} -lt 5 ]
then
while true
do
for i in ${i}
do
printf "%s \t" ${spinner[i]}
sleep .1
printf "\r"
sleep .1
if [ ${i} -eq 0 ]
then
# Here you can clean up or do what to do at zero count
printf "\n"
unset i
unset spinner
return 0 # Can be used in ${?} from parent bash
else
i=$((${i}-1))
fi
done
done
return 1 # Should never be executed
fi
}
# A funny cd ;-)
cd(){
countdown && printf "%s\n" "DONE changing to "${1} # Gives out if return is 0 (${?})
unset cd
cd ${1}
}
#
cd ~
My method of showing progress while sleeping in bash:
sleep 5 | pv -t
It probably can't get any simpler than that :)
Check out this spiner
Or from this project

Nested if statements with nested loops

I am trying to make a simple calculator. I am sure if you even just glanced at this code you will see what I am trying to do.
Enter a number, then choose an operand, then vim should print out a table up to 15 with your number and operand...
Maybe this method is silly, trying to nest a load of loops in nested if statements. But I am new to bash.
The error is 'Unexpected token near else' line 24 but I feel there is a fundamental issue with the nests I do not understand.
Here is current code.
#!/bin/bash
choice=6
read -p "Enter a number bruv" num
#choose operand.
echo "Now choose an operand comrade"
#choices
echo "1. *"
echo "2. +"
echo "3. -"
echo "4. /"
echo "5. ^"
echo -n "Please choose [1,2,3,4,5]"
while [ $choice -eq 6 ]; do
read choice
if [ $choice -eq 1 ] ; then
for((i=0;i<=15;i++))
do
echo -n "$i * $num = $[ $i * $num ] "
echo " "
else
if [ $choice -eq 2 ] ; then
for((i=0;i<=15;i++))
do
echo -n "$i + $num = $[ $i + $num ] "
echo " "
else
if [ $choice -eq 3 ] ; then
for((i=0;i<=15;i++))
do
echo -n "$i - $num = $[ $i - $num ] "
echo " "
else
if [ $choice -eq 4 ] ; then
for((i=0;i<=15;i++))
do
echo -n "$i / $num = $[ $i / $num ] "
echo " "
else
if [ $choice -eq 5 ] ; then
for((i=0;i<=15;i++))
do
echo -n "$i ^$num = $[ $i ^$num ] "
echo " "
else echo "Please choose between 1 and 5!!!"
echo "1. *"
echo "2. +"
echo "3. -"
echo "4. /"
echo "5. ^"
echo -n "Please choose [1,2,3,4,5]"
fi
fi
fi
fi
fi
done
Would it be better to implement this?
# !/bin/bash
# Take user Input
echo "Enter number : "
read a
# Input type of operation
echo "Enter Choice :"
echo "1. Addition"
echo "2. Subtraction"
echo "3. Multiplication"
echo "4. Division"
echo "5. Power"
read ch
# Switch Case to perform
# calulator operations
case $ch in
1)res=`for((i=0;i<=15;i++))
do
echo -n "$i - $num = $[ $i - $num ] "
echo " "`
;;
2)res=`for((i=0;i<=15;i++))
do
echo -n "$i - $num = $[ $i - $num ] "
echo " "`
;;
3)res=`for((i=0;i<=15;i++))
do
echo -n "$i - $num = $[ $i - $num ] "
echo " "`
;;
4)res=`for((i=0;i<=15;i++))
do
echo -n "$i - $num = $[ $i - $num ] "
echo " "c`
;;
esac
echo "Result : $res" ```
Here is a solution, using a function:
#! /bin/bash
ITER_MAX=15
show_values() # n op
{
local n=$1 op=$2
for ((i=0; i<=ITER_MAX; i++)); do
((i>0)) && echo -n " ; "
echo -n "$i $op $n = $((i $op n))"
done
echo
}
# Take user Input
read -p "Enter number : " a
# Input type of operation
echo "Enter Choice (Ctrl+C to stop):"
PS3=">> "
select ch in Addition Subtraction Multiplication Division Power ; do
case "$ch" in
Add*) op="+" ;;
Sub*) op="-" ;;
Mul*) op="*" ;;
Div*) op="/" ;;
Pow*) op="**" ;;
*) echo "Bad choice, abort" >&2 ; break ;;
esac
show_values "$a" "$op"
done
Some explanations:
(( )) is arithmetic evaluation and $(( )) is arithmetic expansion
((i>0)) && echo -n " ; " is equivalent to if ((i>0)); then echo -n " ; " ; fi
read -p "Enter number : " a is equivalent to echo -n "Enter number : " ; read a
about select, see help select in your bash terminal.
Use https://www.shellcheck.net/
Line 20:
for((i=0;i<=15;i++))
^-- SC1009: The mentioned syntax error was in this for loop.
^-- SC1073: Couldn't parse this arithmetic for condition. Fix to allow more checks.
Line 21:
do
^-- SC1061: Couldn't find 'done' for this 'do'.
Line 24:
else
^-- SC1062: Expected 'done' matching previously mentioned 'do'.
^-- SC1072: Unexpected keyword/token. Fix any mentioned problems and try again.
An alternate take:
read -p "Enter a number and an operator: " -a a
for n in {1..15}; do printf "%+10s\n" $((${a[#]} $n)); done
-p tells read to supply a prompt. -a reads into an array (named a here).
{1..15} is built-in bash sequence syntax.
%+10s tells printf to space-pad/right justify out to 10 characters.
${a[#]} is replaced with all the elements of the array - the number and then operator.
$(( ... )) does arithmetic processing and replaces itself with the result (as a string).
So if you enter "10 *" then $((${a[#]} $n)) processes 10 * 1 the first time, so printf "%+10s\n" $((${a[#]} $n)) outputs " 10" with a trailing newline character. Then the loop replaces the 1 with the next number on each iteration, up to 15. This also allows other operators, such as % for modulus, but will crash if something invalid is given.
If you want error checking -
while [[ ! "${a[*]}" =~ ^[0-9][0-9]*\ ([*/+-]|\*\*)$ ]]
do read -p "Enter a, integer and an operator (one of: + - * / **) " -a a
done
for n in {1..15}; do printf "%+10s\n" $((${a[#]} $n)); done
If you want floating point math, try bc:
while [[ ! "${a[*]}" =~ ^[0-9]+.?[0-9]*\ ([*/+-]|\*\*)$ ]]
do read -p "Enter a, integer and an operator (one of: + - * / **) " -a a
done
for n in {1..15}; do printf "%+15s\n" $(bc -l <<< "scale=3;${a[#]} $n"); done
Personally, I'd put the inputs on the command line. Less futzy, though it might require quoting the * operator, depending on how you run it and what's in the directory.
#!/bin/bash
me=${0##*/}
use="
use: $me {number} {operator} [iterations] [scale]
Numbers may include one decimal point.
Operators must be one of: + - * / **
"
while getopts "n:o:i:s:" arg
do case $arg in
n) n="$OPTARG" ;;
o) o="$OPTARG" ;;
i) i="$OPTARG" ;;
s) s="$OPTARG" ;;
*) printf "%s\n" "Invalid argument '$arg' $use";
exit 1;;
esac
done
[[ -n "$n" && -n "$o" ]] || { echo "$use"; exit 1; }
[[ "$n" =~ ^[0-9]+.?[0-9]*$ ]] || { printf "%s\n" "Invalid number '$n' $use"; exit 1; }
[[ "$o" =~ ^([*/^+-]|\*\*)$ ]] || { printf "%s\n" "Invalid operator '$o' $use"; exit 1; }
[[ "${i:=15}" =~ ^[0-9]+.?[0-9.]$ ]] || { printf "%s\n" "Invalid number '$i' $use"; exit 1; }
[[ "${s:=3}" =~ ^[0-9]+$ ]] || { printf "%s\n" "Invalid scale '$s' (must be an integer) $use"; exit 1; }
c=1
while ((c < i))
do printf "%+15s\n" $(bc -l <<< "scale=$s; $n $o $c")
((c++))
done

How to write script for arithmetic mean of even numbers

I have to write a script in bash where I have to provide 10 numbers to the table. Then script have to write the content out and arithmetic mean of even numbers. I did like 90% of the script, but I can't find out how to extract information about quantity of even numbers that is needed for arithmetic mean.
Here is my code:
echo "Provide data:"
i=0
for (( i = 0 ; i < 10; i++ ))
do
echo "Provide $[$i+1] number:"
read x
if [ "$x" = "" ]
then
break
else
table[$i]=$x
fi
done
echo "Provided data: ${table[*]}"
result=0
for (( i = 0 ; i < 10; i++))
do
res=$[${table[i]}%2]
if [ $res -eq 0 ]
then
echo "Number ${table[i]} is even"
result=$[$result+${table[$i]}]
fi
done
echo "SUM:$[$result]"
ignoring data input adding only odd inputs can look like :
$ cat c.sh
#!/bin/bash
declare -A xDarray
sum=0
xDarray[0 1]=1
xDarray[0 2]=3
xDarray[1 0]=2
xDarray[2 0]=4
for var in ${xDarray[#]}
do
if [ $(( $var & 1 )) == 0 ] ; then
echo $var is even
i+=1
tab[$i]=$var
sum=$(( $sum + $var))
fi
done
var=$(echo ${tab[#]} | sed 's/ / + /g' )
echo $var = $sum
result in
$ ./c.sh
2 is even
4 is even
2 + 4 = 6
$
whatever the number of data is used it would work
I let you work around your data input
Here are some suggested modifications for your script, syntax is simple.
#!/bin/bash
arr=()
for (( i=1;i<=10;i++ )); do
number=''
while [[ ! $number =~ ^[0-9]+$ ]]; do
printf "Please enter number $i:\n"
read number
done
arr+=($number)
done
printf "\nProvided numbers:"
printf " %d" "${arr[#]}"
printf "\nEven numbers:"
s=0
n=0
for x in "${arr[#]}"; do
if ! (( x % 2 )); then
printf " %d" "$x"
s=$(( s + x ))
(( n++ ))
fi
done
m=$(( s / n ))
printf "\nMean of the %d even numbers: %d / %d = %d\n" "$n" "$s" "$n" "$m"
Use an array arr to hold the input numbers, declare with arr=(), append numbers with arr+=($x), we refer only ${arr[#]} for all the items and we avoid any other complex array references, indices etc.
Every input number is tested against regular expression ^[0-9]+$ which means one or more digits (and only digits) with the =~ operator, and if this is not true, we prompt again for the same i-th input number.
Also we prefer printf for printing.
The last loop is the standard array loop, where we use again the arithmetic expansion syntax to find the even numbers, to add them to the sum and get the mean of them (result is an integer).
If you want to print a decimal result, e.g. with 2 floating points, you could use bc and printf "%f" like this:
m=$( bc <<< "scale=2; $s/$n" )
printf "%.2f" "$m"

Nested loop in shell

I am a beginner at shell programming. I have a problem related to nested loops in shell scripts. I want to get output like this:
Input: 4
Output:
*
**
***
****
This is the script I am using so far:
echo "input : "
read a
for ((i=0; i<a; i++))
do
for ((j=0; j<i; j++))
do
echo "*"
done
echo "\n"
done
When trying to execute my program I get an error: Bad for looping.
Thanks in advance.
try this
echo "input : "
read a
for ((i=0; i<a; i++))
do
for ((j=0; j<=i; j++))
do
printf "*"
done
echo
done
To not print newlines, you can use printf (or the echo -n but is not as portable as printf)
I don't get any error with the script! Though the echo needs to be different like below:
echo "input : "
read a
for ((i=0; i<a; i++))
do
for ((j=0; j<i; j++))
do
echo -ne "*"
done
echo -ne "\n"
done
You might try adding $ in front of the variables while accessing them though.
It is not giving any errors for me.
#!/bin/bash
print_starry_row()
{
n="$1"
for ((i=0;i<n;i++))
{
echo -n "*"
}
echo
}
read -p "Enter number of stars? " num
if [[ "$num" -eq $num ]]
then
for ((i=1;i<=num;i++))
{
print_starry_row $i
}
else
echo "You must enter a valid integrer"
fi

Shell Script, commandline arguments

the task is to write a shell script
inputs are a string and a number
for example,
xxx.sh "Hello World" 3
the input will be
***************
* Hello World *
* Hello World *
* Hello World *
***************
and here is what have I got so far:
function mantra() {
echo "string is $1"
echo "number is $2"
echo $string
echo $PATH
for num in string_length; do
echo "*"
done
}
How do I count the number of characters in the string?
Am I doing right? I am not exactly sure how to pass command-line arguments into my function.Blockquote
The number of characters in your input string is ${#1}
See this page for a short explanation.
#!/bin/sh
function mantra() {
string=$1
num=$2
strlen=${#string}
let strlen=$strlen+2
echo -n "*"
for (( times = 0; times < $strlen; times++ )); do echo -n "*" ; done
echo "*";
}
mantra $1 $2
for (( times = 0; times < $num; times++ )); do
echo "* $string *"
done
mantra $1 $2

Resources