Shell for loop, stopping at declaration - linux

I'm trying to write a for loop that goes from 1 to 10, then calculates ( 1 through 10 mod 5) + 2. After that I want to display it like this (1 to 10 mod 5) + 2 = answer. However i'm getting an error at the beginning of the loop which is a syntax error.
for (( i = 0; i <= 10; i++)); do
calculate=(i % 5) + 2
echo ("("i "% 5) + 2" calculate)
done

Try these changes:
calculate=$(( i % 5 + 2 ))
# $(( ... )) is the shell's way to do arithmetic
echo "($i % 5) + 2 = " $calculate
# $x is a way to refer to the value of variable x
# (also inside a double-quoted string)
The for loop header is actually OK.

Related

"Attempted assignment to a non-variable" in bash

I'm new to Bash and I've been having issues with creating a script. What this script does is take numbers and add them to a total. However, I can't get total to work.It constantly claims that total is a non-variable despite it being assigned earlier in the program.
error message (8 is an example number being entered)
./adder: line 16: 0 = 0 + 8: attempted assignment to non-variable (error token is "= 0 + 8")
#!/bin/bash
clear
total=0
count=0
while [[ $choice != 0 ]]; do
echo Please enter a number or 0 to quit
read choice
if [[ $choice != 0 ]];
then
$(($total = $total + $choice))
$(($count = $count + 1))
echo Total is $total
echo
echo Total is derived from $count numbers
fi
done
exit 0
Get rid of some of the dollar signs in front of the variable names. They're optional inside of an arithmetic context, which is what ((...)) is. On the left-hand side of an assignment they're not just optional, they're forbidden, because = needs the variable name on the left rather than its value.
Also $((...)) should be plain ((...)) without the leading dollar sign. The dollar sign will capture the result of the expression and try to run it as a command. It'll try to run a command named 0 or 5 or whatever the computed value is.
You can write:
((total = $total + $choice))
((count = $count + 1))
or:
((total = total + choice))
((count = count + 1))
or even:
((total += choice))
((count += 1))

Shell script, stop running if failed

I have a shell script that runs a test repeatedly :
#!/bin/tcsh
set x = 1
while ($x <= 10000)
echo $x
./test
# x += 1
end
I am trying to adapt it to break the loop and stop running if the test failed, i.e. the test executable returned with a non-zero status. I thought the following change would work.
#!/bin/tcsh
set x = 1
set y = 0
while ($x <= 10000 && $y == 0)
echo $x
# y = ./test
# x += 1
end
But, I get error #: Expression syntax
Can you please tell me what did I do wrong, and how to capture the return value of ./test in a variable so I can break the loop, or some other way to break the loop upon encountering the test failure
I'm not a fan of scripting in csh, and I highly advise against it. However, in this case, csh seems to do the right thing, and you can simply do:
#!/bin/tcsh
set x = 1
while ($x <= 10000)
echo $x
./test || break
# x += 1
end

Split string in ksh

I got a string as follow :
foo=0j0h0min0s
What would be the best way to convert it in seconds without using date ?
I tried something like this that sounded pretty nice but no luck :
#> IFS=: read -r j h min s <<<"$foo"
#> time_s=$((((j * 24 + h) * 60 + min) * 60 + s))
ksh: syntax error: `<' unexpected
Any idea is welcome, I just can't use date -d to make conversion as it is not present on the system I am working on.
<<<"$foo" is mainly a bash-ism. It is supported in some/newer ksh. (google 'ksh here string' ).
Your read is trying to split at :, wich is not present in your input
If you first get rid of characters, you can split at blank (as ususal)
and changing the here-string to a here-doc
#!/bin/ksh
foo=1j2h3min4s
read -r j h min s << END
"${foo//[a-z]/ }"
END
# or echo "${foo//[a-z]/ }" | read -r j h min s
time_s=$((((j * 24 + h) * 60 + min) * 60 + s))
echo ">$foo< = >${foo//[a-z]/ }< = $j|$h|$min|$s => >$time_s<"
>1j2h3min4s< = >1 2 3 4 < = "1|2|3|4 " => >93784<
# or using array, easy to assign, more typing where used
typeset -a t=( ${foo//[a-z]/ } )
time_s=$(( (( t[0] * 24 + t[1]) * 60 + t[2]) * 60 + t[3] ))
echo ">$foo< = >${foo//[a-z]/ }< = ${t[0]}|${t[1]}|${t[2]}|${t[3]} => >$time_s<"

Shell scripting, finding average of loop results

I made a loop to find the results of calculating each number from (1 to 10 mod 5) + 2
for (( i = 0; i <= 10; i++))
do
calculate=$(( i % 5 + 2 ))
echo "($i % 5) + 2 = " $calculate
done
average=$(($calculate/10))
echo $average`
My problem is fixing my code so that I can take all the results of the loop and find the average of them
Its returning back 0 for the average
You have to keep a full total - ($calculate/10) is just the last iteration. Keep a running total initialized to zero before the loop total = 0 ... then add the calculated value to the total in each iteration of the loop total = $( $total + $calculate ) Then the average is total/10 (not calculate/10).
#!/bin/bash
total=0
for (( i = 0; i <= 10; i++))
do
calculate=$(( i % 5 + 2 ))
total=$(( $total + $calculate ))
echo "($i % 5) + 2 = " $calculate
done
#average=$(($calculate/10))
average=$(($total/10))
echo $average

Variable as bash array index?

#!/bin/bash
set -x
array_counter=0
array_value=1
array=(0 0 0)
for number in ${array[#]}
do
array[$array_counter]="$array_value"
array_counter=$(($array_counter + 1))
done
When running above script I get the following debug output:
+ array_counter=0
+ array_value=1
+ array=(0 0 0)
+ for number in '${array[#]}'
+ array[$array_counter]=1
+ array_counter=1
+ for number in '${array[#]}'
+ array[$array_counter]=1
+ array_counter=2
+ for number in '${array[#]}'
+ array[$array_counter]=1
+ array_counter=3
Why does the variable $array_counter not expand when used as index in array[]?
Bash seems perfectly happy with variables as array indexes:
$ array=(a b c)
$ arrayindex=2
$ echo ${array[$arrayindex]}
c
$ array[$arrayindex]=MONKEY
$ echo ${array[$arrayindex]}
MONKEY
Your example actually works.
echo ${array[#]}
confirms this.
You might try more efficient way of incrementing your index:
((array_counter++))

Resources