Passing value of a variable to another variable - bash

I'm trying to make a function that will output fibonacci numbers. This is the code:
#!/bin/bash
a1=0;
a2=1;
echo "Vnesi n"
read n
echo $a1
echo $a2
for ((i = 1; i <= $n; i++)) do
a3=$(($a1+$a2))
echo $a3
$a1=$a2
$a2=$a3
done
When I run it, it gets to to line 10 (echo $a3) and then outputs an error:
1
0
1
1
./fib.sh: line 11: 0=1: command not found
./fib.sh: line 12: 1=1: command not found
Basically what I'm trying to do is to pass value from a2 to a1 and value from a3 to a2. What am I doing wrong here?

Your first variable assignments are correct:
a1=0
a2=1
The second ones incorrectly prefix the left-hand side with a dollar sign:
$a1=$a2 # Should be a1=$a2
$a2=$a3 # Should be a2=$a3

replace
$a1=$a2
$a2=$a3
with
a1=$a2
a2=$a3

a1=$a2
not $a1
otherwise the left side is evaluated and the value (0 or whatever) is used

Related

Bash for loop skip first element of array

In bash,
I have a string, I want to convert in array and use it in a for loop but skipping the first element. The code below does not works:
build_string="100 99 98"
build_list=("$build_string")
echo $build_list
for i in "${build_list[#]:1}"
do echo "i: " $i
done
The for loop does not print anything. Could you help me? Thanks.
I believe you are not converting the array properly (or at all).
Please see this snippet:
build_string="100 99 98"
#build_list=("$build_string") <-- this is not converting into array, following line is.
IFS=' ' read -r -a build_list <<< "$build_string"
echo $build_list
for i in "${build_list[#]:1}"
do echo "i: " $i
done
sleep 2
now the output is:
100
i: 99
i: 98
Which sounds reasonable. The 100 is printed when you ask echo $build_string.
Reference: split string into array in bash
As pointed out by prefire, the double quotes on the second line are preventing array conversion. This snippet also works:
build_string="100 99 98"
build_list=($build_string)
echo $build_list
for i in "${build_list[#]:1}"
do echo "i: " $i
done
sleep 2
Note: I've added a sleep 2 at the end, so I can see what is being printed.
Replace the second line with this:
build_list=($build_string)
build_list=("$build_string")
build_list array only have one item, so
${build_list[#]:1 is empty.

syntax error: operand expected (error token is "+ ") in bash

I am trying to calculate the sum of numbers entered via command line to my script file. Here is my code
#!/bin/bash
for ((i=0;i<=$#;i++))
do
sum=$(($i + $sum))
done
echo $sum | bc
My terminal input is
bash file.sh 1 2
So the output should be 3 but I am getting
syntax error: operand expected (error token is "+ ")
The actual error reason is because of uninitialized variable sum going through the first iteration of the loop. Initialize the variable before entering the loop.
Also a major logical flaw is that you are not even iterating over the input arguments, but just over the counter i which will produce incorrect results if you pass arguments other than 1 2 from the command-line.
You need to pass over the actual arguments argc and argv (arg count and arg vector: for understanding purposes only) and you don't need bc at all
argc=$#
argv=("$#")
sum=0
for ((i=0; i<${argc}; i++)); do
sum=$((${argv[i]} + $sum))
done
To loop over all command line arguments, you can use the simplified form of the shell for statement:
sum=0
for i do
((sum += i))
done
((sum+=i)) is accepted by bash and many other shells; for a Posix-compatible shell, you can use arithmetic expansion with the : builtin:
: $((sum += i))

Count IP addresses from list

My goal is to take a list of IPs, sometimes the list is just a single IP or it could be a range of IPs and count how many IPs there are in a list. We have another product that generates a list of IPs and we have been manually counting it using an Excel spreadsheet.
Using an existing post here on Stack, I have been attempting to incorporate it into a script that will accept a list. https://codegolf.stackexchange.com/questions/28760/how-many-ip-addresses-are-in-a-given-range (See Pure Bash, 66 bytes).
Instead of grabbing two arguments on the command line $1 and $2, I pull a list into an array then enumerate through the array. If it is a single IP, it just adds 1 to the counter variable, if it is a range it uses the logic to convert the IPs to hex and counts them. I'm running into an issue where I am receiving errors.
I can't seem to figure out why this says "invalid number: printf: 229". I've read up on the expansion principle and I cannot seem to get my head around why it keeps throwing this error and calculating it improperly.
I've used this site for years, this is my first post. Any help would be greatly appreciated!
Thank you!
This is what I have so far:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "Please supply a list of IP addresses"
echo "Example: " $0 "list.txt"
exit
fi
#Set some variables
LIST=($(cat ./$1))
COUNT=0
# Function for removing dots and converting to hex
p()(printf %02x ${1//./ })
# Enumerate the array of IPs
for RANGE in ${LIST[#]};do
IFS=- read IP1 IP2 <<< $RANGE
if [ -z $IP2 ]; then
COUNT=$[COUNT + 1]
else
r=$[0x`p $IP1`-0x`p $IP2`]
COUNT=$[COUNT + $[1+${r/-}]]
fi
done
echo "The count is" $COUNT
sample_range.txt:
192.168.47.11
192.168.48.10
192.168.65.228-192.168.65.229
192.168.65.234
192.168.65.239
192.168.65.241
192.168.65.244
192.168.80.220
192.168.93.231-192.168.93.235
192.168.93.237-192.168.93.239
192.168.103.222
This should result in 18, instead it gives me this output:
# ./rc.sh sample_range.txt
: invalid number: printf: 229
: invalid number: printf: 235
: invalid number: printf: 239
The count is 707
IPs are numbers base 256.
#!/bin/bash
ipdiff() {
declare -i dec1 dec2 diff # set integer attribute
dec1=$1*256*256*256+$2*256*256+$3*256+$4
dec2=$5*256*256*256+$6*256*256+$7*256+$8
diff=$dec2-$dec1+1
counter=counter+$diff
}
declare -i counter
# read IP(s) from file, use . and - as separator
while IFS=".-" read -r a1 a2 a3 a4 b1 b2 b3 b4; do
if [[ -z $b1 ]]; then # $b1 is empty (line with one IP)
counter=counter+1
else # $b1 is not empty (line with 2 IPs)
ipdiff $a1 $a2 $a3 $a4 $b1 $b2 $b3 $b4
fi
done < file
echo $counter
Output:
18

How to assign array length to a variable in bash

I'm trying to write a simple bash script that adds integers and supplies the sum. I figured the easiest way would be to assign the input to an array. Then traverse the array to perform the summation. I need to use the length of the array in my for loop and cannot figure out how to assign the array length to a variable.
Any help appreciated on the simple script (which I did to learn bash)
#!/bin/bash
# add1 : adding user supplied ints
echo -n "Please enter any number of integers: "
read -a input
echo "Your input is ${input[*]}"
echo "${#input[#]} number of elements"
num = ${#input[#]} # causing error
for ((i = 0; i < "${num}"; ++i )); do # causing error
sum = $((sum + input[$i]))
done
echo "The sum of your input is $sum"
Which yields the errors:
line 10: num: command not found
line 11: ((: i < :syntax error: operand expected (error token is "< ")
You just have a syntax error. Remove the space before =:
num = ${#input[#]} # causing error
becomes:
num=${#input[#]} # works
Note that if you assign to a variable in bash using the = operator, there MUST NOT be any space before and after the =
Read this entry about Variable Assignment in the Advanced Bash-Scripting Guide

bash Command line substitution in for loop

I have a test script test.sh where I am trying to print out command line arguments.to the script but the following does not work as expected
`#!/bin/bash
for((i=1;i<"$#";i++)) do
printf "Position %s of argumentArray has %s \n", $i $(($i))
done`
( my idea was that the (()) will do the mathematical evaluation resulting in $1 $2 etc.) Neither does
for((i=1;i<"$#";i++)) do
printf "Position %s of argumentArray has %s \n", $i $"$( eval echo $i )"
done
both gives as out put when run as follows
./test.sh first second third
Position 1 of argumentArray has 1
Position 1 of argumentArray has 2
Position 1 of argumentArray has 3
instead of
Position 1 of argumentArray has first
Position 1 of argumentArray has second
Position 1 of argumentArray has third
I face the same problem with
for((i=1;i<="$#";i++))
do
case "$($i)" in
.......
case evaluates to 1 ,2 3 etc insted of the actual parameter passed in.
Please help me in understanding where I am going wrong.
You can use indirect expansion to do this fairly easily:
for((i=1;i<=$#;i++)) do
printf "Position %s of argumentArray has %s \n" $i "${!i}"
done
I also fixed some minor problems in the above: the loop end condition should be i<=$# to print the last arg, there shouldn't be a comma after the format string for printf, and there should be double-quotes around the argument ("${!i}") in case it has any spaces or other funny characters.
The commandline arguments can be accessed directly but if you want them by position you can do this:
arguments=($#)
for ((i = 0; i < ${#arguments[#]}; i++)); do
echo "arguments[$i] = ${arguments[$i]}"
done
A run of the script:
$ ./args.sh first second third
arguments[0] = first
arguments[1] = second
arguments[2] = third
ADDENDUM
Information on Bash arrays:
Array chapter from the Advanced Bash Scripting Guide
15 Array tutorials

Resources