How to add the sum of several squares (bourne scripting) - linux

I'm trying to write a script that takes a list of integers as command line arguments, calculates the square of each integer, then gives me the sum of the squares. Here's what I have so far...
if [ $# = 0 ]
then
echo "Usage: $0 integer-list"
exit 1
fi
for list in "$#"
do
echo "The square of $list is: $(($list*$list))"
done
As you can see, I have a simple for loop to deal with the squares, I'm just not sure how to get the sum of those squares and echo that to the screen. Any suggestions?

Just add a sum of previous squares to current square of item from the list.
#!/bin/bash
if [ $# = 0 ]
then
echo "Usage: $0 integer-list"
exit 1
fi
SUM=0
for ITEM in "$#"
do
SUM=$(($SUM+$ITEM*$ITEM))
done
echo "Sum of squares is :" $SUM

On the command line -
$: for n in 2 3 4
> do q=$((n*n))
> echo "square of $n is $q"
> s=$((s+q))
> done; echo "Sum of squares: $s"
square of 2 is 4
square of 3 is 9
square of 4 is 16
Sum of squares: 29
In a script, you can just say
for n in "$#"

Related

Linux if-else not working

i write one linux code for Fibonacci series(0 1 1 2 3 5 8) but when i run its always showing me else statement.
#!/bin/bash
#This program will show series of Fibonacci numbers upto user input.
echo -n "Enter the number for Fibonacci series: "
read num
if [ $# = 1 ]
then
x1 = 0
x2 = 1
echo "The Fibonacci series for the number $num is: "
for (( y=0;$y<num; y=$y+1 ))
do
echo -n "$x1 "
x2 = $(( $x2 + $1 ))
x1 = $(( $x2 - $x1 ))
done
else
echo "Input is wrong"
fi
if i remove if satement then i got error of line 10 and 11 and of for loop
I'm not sure why you're checking the number of arguments, but you either need to provide one argument or change it to check for zero arguments. Also you have a few spaces that will cause problems. You'll need to remove those. Lastly, as #bnaecker mentioned, you'll want to use -eq instead of = when comparing numerical equality.
#!/bin/bash
#This program will show series of Fibonacci numbers upto user input.
echo -n "Enter the number for Fibonacci series: "
read num
if [ $# = 0 ]
then
x1=0
x2=1
echo "The Fibonacci series for the number $num is: "
for (( y=0;$y<num; y=$y+1 ))
do
echo -n "$x1 "
x2=$(($x2 + $1))
x1=$(($x2 - $x1))
done
else
echo "Input is wrong"
fi

Pascal's triangle in Linux shell script

I'm trying to write a code which receives an integer "n" as a parameter and then print the n-th row of the Pascal's triangle starting from 0, 1,..,n.
for example if the entry is 3, the program prints 1 3 3 1.
So far I wrote a code to get the whole triangle printed, but I can't have just the last row.
This is what I have
echo "Insert the row:" read n for((i=0;i<$n;i++))
do
eval"a$i=($(w=1;v=1
for((j=0;j<$n-$i;j++))
do
[ $i -eq 0 -o $j -eq 0 ]&&{ v=1 && w=1; }||v=$((w+a$((i-1))[$((j))]))
echo -n "$v "
w=$v
done))"
eval echo "$(for((k=0;k<=$i;k++))
do
eval "echo -n \"\$((a\$((i-k))[k])) \""
done)"
done
#!/bin/bash
read -p "Insert the row:" n
typeset -A Tab
for((i=0;i<=$n;i++))
do
Tab[$i,0]=1
Tab[$i,$i]=1
for((j=1;j<$i;j++))
do
a=${Tab[$((i-1)),$((j-1))]}
b=${Tab[$((i-1)),$j]}
Tab[$i,$j]=$(( a + b ))
done
done
#print result
for((j=0;j<=$n;j++))
do
echo -n ${Tab[$n,$j]} " "
done
echo
Test :
Insert the row:3
1 3 3 1
I found an awk solution to that question:
awk -v line_num=5 'BEGIN{for(i=line_num;i<=line_num;i++){c=1;r=c;for(j=0;j<i;j++){c*=(i-j)/(j+1);r=r" "c};print r}}'
Change line_num value to the desired one.
Based on a solution found here.
That's of course if awk counts…
Here is a simple bash script to print pascal's triangle using simple for,if else and echo command:
echo "Enter number of rows : "
read rows
coef=1
for((i=0;i<rows;i++))
do
for((space=1;space<=rows-i; space++))
do
echo -n " "
done
for((j=0;j<=i;j++))
do
if [ $j -eq 0 -o $i -eq 0 ]
then
coef=1;
else
coef=$((coef*(i-j+1)/j))
fi
echo -n $coef " "
done
echo
done

Finding numbers in file (script, bash)

I want to find number or words in file. As first parameter it gets file name and second number you are looking for.
For example I write in command line:
bash script.sh file.txt 6
And i get on output
Number 6 repeats 4 time
This is content in file.txt
5 4 5 6 2 4 6 3 6 6
This is the code what I came up and stuck
para2=$2
while read line
do
array=($line)
echo "Value of third element in my array : ${array[3]} "
done < $1
I dont know how to compare parameter 2 with every array. I know that in code above I print out third array but I dont know how to go through every array and compare them with parameter two. I mean i want to go through all numbers and compare with input parameter. Pleas help
Try this:
numOccurences=0
while read line
do
array=($line)
for i in "${array[#]}"
do
if [ "$2" = "$i" ]
then
numOccurences=`expr $numOccurences + 1`
fi
done
done < $1
echo "$2 occurs $numOccurences times in $1"
The program will read a line, iterate through the array formed by the line, and then it will compare the value to the target character. A counter is updated for every match, and the result is printed at the end.
Example input (file.txt):
5 4 5 6 2 4 6 3 6 6 6 6
6 6
᠎
Command:
/Users/Robert/Desktop/Untitled.sh /Users/Robert/Desktop/file.txt 6
Output:
6 occurs 8 times in /Users/Robert/Desktop/file.txt
para2=$2
counter=0
while read line
do
for num in $line
do
if [[ $num -eq $para2 ]]
then let counter = ((counter + 1))
fi
done
done < "$1"
echo Number $para2 repeats $counter times
#!/bin/bash
echo "Number to be searched $2 "
echo "File name passed : $1"
filename=$1
count=0
while read line
do
for word in $line; do
#echo "Number = $word"
if [ "$2" == "$word" ]; then
count=$(expr $count + 1)
fi
done
done < $filename
echo $2 is observed $count times

How to declare 2D array in bash

I'm wondering how to declare a 2D array in bash and then initialize to 0.
In C it looks like this:
int a[4][5] = {0};
And how do I assign a value to an element? As in C:
a[2][3] = 3;
You can simulate them for example with hashes, but need care about the leading zeroes and many other things. The next demonstration works, but it is far from optimal solution.
#!/bin/bash
declare -A matrix
num_rows=4
num_columns=5
for ((i=1;i<=num_rows;i++)) do
for ((j=1;j<=num_columns;j++)) do
matrix[$i,$j]=$RANDOM
done
done
f1="%$((${#num_rows}+1))s"
f2=" %9s"
printf "$f1" ''
for ((i=1;i<=num_rows;i++)) do
printf "$f2" $i
done
echo
for ((j=1;j<=num_columns;j++)) do
printf "$f1" $j
for ((i=1;i<=num_rows;i++)) do
printf "$f2" ${matrix[$i,$j]}
done
echo
done
the above example creates a 4x5 matrix with random numbers and print it transposed, with the example result
1 2 3 4
1 18006 31193 16110 23297
2 26229 19869 1140 19837
3 8192 2181 25512 2318
4 3269 25516 18701 7977
5 31775 17358 4468 30345
The principle is: Creating one associative array where the index is an string like 3,4. The benefits:
it's possible to use for any-dimension arrays ;) like: 30,40,2 for 3 dimensional.
the syntax is close to "C" like arrays ${matrix[2,3]}
Bash doesn't have multi-dimensional array. But you can simulate a somewhat similar effect with associative arrays. The following is an example of associative array pretending to be used as multi-dimensional array:
declare -A arr
arr[0,0]=0
arr[0,1]=1
arr[1,0]=2
arr[1,1]=3
echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1
If you don't declare the array as associative (with -A), the above won't work. For example, if you omit the declare -A arr line, the echo will print 2 3 instead of 0 1, because 0,0, 1,0 and such will be taken as arithmetic expression and evaluated to 0 (the value to the right of the comma operator).
Bash does not support multidimensional arrays.
You can simulate it though by using indirect expansion:
#!/bin/bash
declare -a a0=(1 2 3 4)
declare -a a1=(5 6 7 8)
var="a1[1]"
echo ${!var} # outputs 6
Assignments are also possible with this method:
let $var=55
echo ${a1[1]} # outputs 55
Edit 1: To read such an array from a file, with each row on a line, and values delimited by space, use this:
idx=0
while read -a a$idx; do
let idx++;
done </tmp/some_file
Edit 2: To declare and initialize a0..a3[0..4] to 0, you could run:
for i in {0..3}; do
eval "declare -a a$i=( $(for j in {0..4}; do echo 0; done) )"
done
Another approach is you can represent each row as a string, i.e. mapping the 2D array into an 1D array. Then, all you need to do is unpack and repack the row's string whenever you make an edit:
# Init a 4x5 matrix
a=("00 01 02 03 04" "10 11 12 13 14" "20 21 22 23 24" "30 31 32 33 34")
aset() {
row=$1
col=$2
value=$3
IFS=' ' read -r -a rowdata <<< "${a[$row]}"
rowdata[$col]=$value
a[$row]="${rowdata[#]}"
}
aget() {
row=$1
col=$2
IFS=' ' read -r -a rowdata <<< "${a[$row]}"
echo ${rowdata[$col]}
}
aprint() {
for rowdata in "${a[#]}"; do
echo $rowdata
done
}
echo "Matrix before change"
aprint
# Outputs: a[2][3] == 23
echo "a[2][3] == $( aget 2 3 )"
echo "a[2][3] = 9999"
aset 2 3 9999
# Show result
echo "Matrix after change"
aprint
Outputs:
Matrix before change
00 01 02 03 04
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34
a[2][3] == 23
a[2][3] = 9999
Matrix after change
00 01 02 03 04
10 11 12 13 14
20 21 22 9999 24
30 31 32 33 34
You can also approach this in a much less smarter fashion
q=()
q+=( 1-2 )
q+=( a-b )
for set in ${q[#]};
do
echo ${set%%-*}
echo ${set##*-}
done
of course a 22 line solution or indirection is probably the better way to go and why not sprinkle eval every where to .
2D array can be achieved in bash by declaring 1D array and then elements can be accessed using (r * col_size) + c). Below logic delcares 1D array (str_2d_arr) and prints as 2D array.
col_size=3
str_2d_arr=()
str_2d_arr+=('abc' '200' 'xyz')
str_2d_arr+=('def' '300' 'ccc')
str_2d_arr+=('aaa' '400' 'ddd')
echo "Print 2D array"
col_count=0
for elem in ${str_2d_arr[#]}; do
if [ ${col_count} -eq ${col_size} ]; then
echo ""
col_count=0
fi
echo -e "$elem \c"
((col_count++))
done
echo ""
Output is
Print 2D array
abc 200 xyz
def 300 ccc
aaa 400 ddd
Below logic is very useful to get each row from the above declared 1D array str_2d_arr.
# Get nth row and update to 2nd arg
get_row_n()
{
row=$1
local -n a=$2
start_idx=$((row * col_size))
for ((i = 0; i < ${col_size}; i++)); do
idx=$((start_idx + i))
a+=(${str_2d_arr[${idx}]})
done
}
arr=()
get_row_n 0 arr
echo "Row 0"
for e in ${arr[#]}; do
echo -e "$e \c"
done
echo ""
Output is
Row 0
abc 200 xyz
A way to simulate arrays in bash (it can be adapted for any number of dimensions of an array):
#!/bin/bash
## The following functions implement vectors (arrays) operations in bash:
## Definition of a vector <v>:
## v_0 - variable that stores the number of elements of the vector
## v_1..v_n, where n=v_0 - variables that store the values of the vector elements
VectorAddElementNext () {
# Vector Add Element Next
# Adds the string contained in variable $2 in the next element position (vector length + 1) in vector $1
local elem_value
local vector_length
local elem_name
eval elem_value=\"\$$2\"
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
vector_length=$(( vector_length + 1 ))
elem_name=$1_$vector_length
eval $elem_name=\"\$elem_value\"
eval $1_0=$vector_length
}
VectorAddElementDVNext () {
# Vector Add Element Direct Value Next
# Adds the string $2 in the next element position (vector length + 1) in vector $1
local elem_value
local vector_length
local elem_name
eval elem_value="$2"
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
vector_length=$(( vector_length + 1 ))
elem_name=$1_$vector_length
eval $elem_name=\"\$elem_value\"
eval $1_0=$vector_length
}
VectorAddElement () {
# Vector Add Element
# Adds the string contained in the variable $3 in the position contained in $2 (variable or direct value) in the vector $1
local elem_value
local elem_position
local vector_length
local elem_name
eval elem_value=\"\$$3\"
elem_position=$(($2))
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
if [ $elem_position -ge $vector_length ]; then
vector_length=$elem_position
fi
elem_name=$1_$elem_position
eval $elem_name=\"\$elem_value\"
if [ ! $elem_position -eq 0 ]; then
eval $1_0=$vector_length
fi
}
VectorAddElementDV () {
# Vector Add Element
# Adds the string $3 in the position $2 (variable or direct value) in the vector $1
local elem_value
local elem_position
local vector_length
local elem_name
eval elem_value="$3"
elem_position=$(($2))
eval vector_length=\$$1\_0
if [ -z "$vector_length" ]; then
vector_length=$((0))
fi
if [ $elem_position -ge $vector_length ]; then
vector_length=$elem_position
fi
elem_name=$1_$elem_position
eval $elem_name=\"\$elem_value\"
if [ ! $elem_position -eq 0 ]; then
eval $1_0=$vector_length
fi
}
VectorPrint () {
# Vector Print
# Prints all the elements names and values of the vector $1 on sepparate lines
local vector_length
vector_length=$(($1_0))
if [ "$vector_length" = "0" ]; then
echo "Vector \"$1\" is empty!"
else
echo "Vector \"$1\":"
for ((i=1; i<=$vector_length; i++)); do
eval echo \"[$i]: \\\"\$$1\_$i\\\"\"
###OR: eval printf \'\%s\\\n\' \"[\$i]: \\\"\$$1\_$i\\\"\"
done
fi
}
VectorDestroy () {
# Vector Destroy
# Empties all the elements values of the vector $1
local vector_length
vector_length=$(($1_0))
if [ ! "$vector_length" = "0" ]; then
for ((i=1; i<=$vector_length; i++)); do
unset $1_$i
done
unset $1_0
fi
}
##################
### MAIN START ###
##################
## Setting vector 'params' with all the parameters received by the script:
for ((i=1; i<=$#; i++)); do
eval param="\${$i}"
VectorAddElementNext params param
done
# Printing the vector 'params':
VectorPrint params
read temp
## Setting vector 'params2' with the elements of the vector 'params' in reversed order:
if [ -n "$params_0" ]; then
for ((i=1; i<=$params_0; i++)); do
count=$((params_0-i+1))
VectorAddElement params2 count params_$i
done
fi
# Printing the vector 'params2':
VectorPrint params2
read temp
## Getting the values of 'params2'`s elements and printing them:
if [ -n "$params2_0" ]; then
echo "Printing the elements of the vector 'params2':"
for ((i=1; i<=$params2_0; i++)); do
eval current_elem_value=\"\$params2\_$i\"
echo "params2_$i=\"$current_elem_value\""
done
else
echo "Vector 'params2' is empty!"
fi
read temp
## Creating a two dimensional array ('a'):
for ((i=1; i<=10; i++)); do
VectorAddElement a 0 i
for ((j=1; j<=8; j++)); do
value=$(( 8 * ( i - 1 ) + j ))
VectorAddElementDV a_$i $j $value
done
done
## Manually printing the two dimensional array ('a'):
echo "Printing the two-dimensional array 'a':"
if [ -n "$a_0" ]; then
for ((i=1; i<=$a_0; i++)); do
eval current_vector_lenght=\$a\_$i\_0
if [ -n "$current_vector_lenght" ]; then
for ((j=1; j<=$current_vector_lenght; j++)); do
eval value=\"\$a\_$i\_$j\"
printf "$value "
done
fi
printf "\n"
done
fi
################
### MAIN END ###
################
If each row of the matrix is the same size, then you can simply use a linear array and multiplication.
That is,
a=()
for (( i=0; i<4; ++i )); do
for (( j=0; j<5; ++j )); do
a[i*5+j]=0
done
done
Then your a[2][3] = 3 becomes
a[2*5+3] = 3
This approach might be worth turning into a set of functions, but since you can't pass arrays to or return arrays from functions, you would have to use pass-by-name and sometimes eval. So I tend to file multidimensional arrays under "things bash is simply Not Meant To Do".
One can simply define two functions to write ($4 is the assigned value) and read a matrix with arbitrary name ($1) and indexes ($2 and $3) exploiting eval and indirect referencing.
#!/bin/bash
matrix_write () {
eval $1"_"$2"_"$3=$4
# aux=$1"_"$2"_"$3 # Alternative way
# let $aux=$4 # ---
}
matrix_read () {
aux=$1"_"$2"_"$3
echo ${!aux}
}
for ((i=1;i<10;i=i+1)); do
for ((j=1;j<10;j=j+1)); do
matrix_write a $i $j $[$i*10+$j]
done
done
for ((i=1;i<10;i=i+1)); do
for ((j=1;j<10;j=j+1)); do
echo "a_"$i"_"$j"="$(matrix_read a $i $j)
done
done
Mark Reed suggested a very good solution for 2D arrays (matrix)! They always can be converted in a 1D array (vector). Although Bash doesn't have a native support for 2D arrays, it's not that hard to create a simple ADT around the mentioned principle.
Here is a barebone example with no argument checks, etc, just to keep the solution clear: the array's size is set as two first elements in the instance (documentation for the Bash module that implements a matrix ADT, https://github.com/vorakl/bash-libs/blob/master/src.docs/content/pages/matrix.rst )
#!/bin/bash
matrix_init() {
# matrix_init instance x y data ...
declare -n self=$1
declare -i width=$2 height=$3
shift 3;
self=(${width} ${height} "$#")
}
matrix_get() {
# matrix_get instance x y
declare -n self=$1
declare -i x=$2 y=$3
declare -i width=${self[0]} height=${self[1]}
echo "${self[2+y*width+x]}"
}
matrix_set() {
# matrix_set instance x y data
declare -n self=$1
declare -i x=$2 y=$3
declare data="$4"
declare -i width=${self[0]} height=${self[1]}
self[2+y*width+x]="${data}"
}
matrix_destroy() {
# matrix_destroy instance
declare -n self=$1
unset self
}
# my_matrix[3][2]=( (one, two, three), ("1 1" "2 2" "3 3") )
matrix_init my_matrix \
3 2 \
one two three \
"1 1" "2 2" "3 3"
# print my_matrix[2][0]
matrix_get my_matrix 2 0
# print my_matrix[1][1]
matrix_get my_matrix 1 1
# my_matrix[1][1]="4 4 4"
matrix_set my_matrix 1 1 "4 4 4"
# print my_matrix[1][1]
matrix_get my_matrix 1 1
# remove my_matrix
matrix_destroy my_matrix
For simulating a 2-dimensional array, I first load the first n-elements (the elements of the first column)
local pano_array=()
i=0
for line in $(grep "filename" "$file")
do
url=$(extract_url_from_xml $line)
pano_array[i]="$url"
i=$((i+1))
done
To add the second column, I define the size of the first column and calculate the values in an offset variable
array_len="${#pano_array[#]}"
i=0
while [[ $i -lt $array_len ]]
do
url="${pano_array[$i]}"
offset=$(($array_len+i))
found_file=$(get_file $url)
pano_array[$offset]=$found_file
i=$((i+1))
done
The below code will definitely work provided if you are working on a Mac you have bash version 4. Not only can you declare 0 but this is more of a universal approach to dynamically accepting values.
2D Array
declare -A arr
echo "Enter the row"
read r
echo "Enter the column"
read c
i=0
j=0
echo "Enter the elements"
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo $i $j
read m
arr[${i},${j}]=$m
j=`expr $j + 1`
done
i=`expr $i + 1`
done
i=0
j=0
while [ $i -lt $r ]
do
j=0
while [ $j -lt $c ]
do
echo -n ${arr[${i},${j}]} " "
j=`expr $j + 1`
done
echo ""
i=`expr $i + 1`
done

Syntax error near unepected token do

This is my code for a bubble sort on n numbers:
#!/bin/bash
echo -n "Input n, the number of numbers"
read N
declare -a array[N]
echo -e "Input the elements, press enter after each element"
for i in seq 1 $N
do
read array[$i]
done
swap1()
{ # for swapping two numbers, we are employing bubble sort
local temp = ${array[$1]}
array[$1] = ${array[$2]}
array[$2]=$temp
return
}
numb_elements=${#array[#]}
let "comparisons = $numb_elements - 1"
count=1
while [ "$comparisons" -gt 0]
do
index =0
while[ "$index" -lt "$comparisons" ];do
if [ ${array[$index]} \> ${array[ 'expr $index + 1']} ]
then
swap1 $index 'expr $index + 1'
fi
let "index += 1" # Or, index+=1 on Bash, ver. 2.1 or newer
done
let "comparisons -=1"
echo
echo "$count: ${array[#]}
echo
let "count +=1"
done
exit 0
I have two problems with this code:
the input array just takes 3 numbers
and then i get an error on line 42 saying syntax error for the command while do
I have tried while [] ; do, but it doesn't work.
Its just been a day that i have been trying bash syntax.
Moreover do not write
for i in seq 1 $N
which iterate i over the set {"seq","1",$N}, but type
for i in $(seq 1 $N)
to insert the result of the command as part of code.
You forgot the closing quote in this line :
echo "$count: ${array[#]}
Also the code of the nested loops is badly indented, so it is a bit hard to read and debug.
So far I have found the following errors:
while [ "$comparisons" -gt 0 ]
^ missing space here
while [ "$index" -lt "$comparisons" ];do
^ missing space
echo "$count: ${array[#]}"
^ missing quote
Note that in bash [ is equivalent to test command, so a space is required around [ and ] unlike many other programming languages.
You made a series of errors:
correct spaces are fundamental to shell scripting
missing `` apices to execute code and get the output
logic error (starting inserting from the second array element and using it from the first one)
iterating the wrong number of time for the bubblesort alg
This is your code corrected.
#!/bin/bash
swap1() { # for swapping two numbers, we are employing bubble sort
local temp=${array[$1]}
array[$1]=${array[$2]}
array[$2]=$temp
return
}
echo -n "Input n, the number of numbers: "
read N
declare -a array[$N]
echo -e "Input the elements, press enter after each element"
for i in `seq 1 $N`
do
read array[$i]
done
numb_elements=${#array[#]}
#let "comparisons = $numb_elements - 1"
comparisons=$numb_elements
count=1
while [ "$comparisons" -gt 0 ]
do
index=1
while [ "$index" -lt "$comparisons" ]
do
tmp=`expr $index + 1`
if [ ${array[$index]} -gt ${array[$tmp]} ]
then
swap1 $index $tmp
fi
let "index += 1" # Or, index+=1 on Bash, ver. 2.1 or newer
done
let "comparisons -= 1"
echo
echo "$count: ${array[#]}"
echo
let "count += 1"
done
exit 0
Try this:
while [ "$comparisons" -gt 0]
should be (notice space before the closing bracket ]):
while [ "$comparisons" -gt 0 ]

Resources