Bash syntax errors like `syntax error: operand expected (error token is "{1 % 2 ")` - bash

Please tell why printing odd numbers in bash script with the following code gives the error:
line 3: {1 % 2 : syntax error: operand expected (error token is "{1 % 2 ")
for i in {1 to 99}
do
rem=$(( $i % 2 ))
if [$rem -neq 0];
then
echo $i
fi
done

This is working example:
for i in {1..99}
do
rem=$(($i % 2))
if [ "$rem" -ne "0" ]; then
echo $i
fi
done
used for loop have a typo in minimum and maximum number, should be {1..99} instead of {1 to 99}
brackets of the if statement needs to be separated with whitespace character on the left and on the right side
Comparision is done with ne instead of neq, see this reference.
As already pointed out, you can use this shell checker if you need some clarification of the error you get.

Not really sure why nobody included it, but this works for me and is simpler than the other 'for' solutions:
for (( i = 1; i < 100; i=i+2 )); do echo $i ; done

To print odd numbers between 1 to 99
seq 1 99 | sed -n 'p;n'
With GNU seq, credit to gniourf-gniourf
seq 1 2 99
Example
$ seq 1 10 | sed -n 'p;n'
1
3
5
7
9
if you reverse it will print even
$ seq 1 10 | sed -n 'n;p'
2
4
6
8
10

One liner:
for odd in {1..99..2}; do echo "${odd}"; done
Or print in a cluster.
for odd in {1..99..2}; do echo -n " ${odd} "; done
Likewise, to print even numbers only:
for even in {2..100..2}; do echo "${even}"; done
OR
for even in {2..100..2}; do echo -n " ${even} "; done

Replace {1 to 99} by {1..99}.

for (( i=1; i<=100; i++ ))
do
((b = $i % 2))
if [ $b -ne 0 ]
then
echo $i
fi
done

for i in {1..99}
do
rem=`expr $i % 2`
if [ $rem == 1 ]
then
echo "$i"
fi
done

for i in {0..49}
do
echo $(($i*2+1))
done

Related

BASH , for loop subtracting a number

In BASH ,
I'm trying to create a for loop that subtracting a number by 2
( lest say that number=8 , so I'd like to print 6, ,4 , 2 )
What is wrong with this syntax ?
for (( $number; $number>= 0;$number --2));
do
echo "$number"
done
`
Thanks
I suggest:
#!/bin/bash
number=8
dec=2
for ((i=$number-$dec; i>0; i=$i-$dec)); do
echo "$i"
done
Output:
6
4
2
The correct syntax would be:
for (( number=8; number>= 0; number=number-2))
do
echo "$number"
done
Output:
8
6
4
2
0
but if you only wanted 6, 4, 2, you'd need to change the range:
# here v v and here
for (( number=6; number>=2; number=number-2))
...
Another syntax would be:
$ for number in {6..2..2}
do
echo "$number"
done
When you want to save characters writing ((number=number-2)) you could do
for (( number=8; number>0; number-=2)); do
echo "$number"
done
Be aware of the context, it can go wrong:
number=8
while (( number-=2 && number>0 )); do # Example of Wrong use
echo "$number"
done
For this solution you need additional parentheses
number=8
while (( ((number-=2)) && number>0 )); do
echo "$number"
done
These parentheses makes it very error prone, so do not use it. I upvoted the solution of #Cyrus.

unix shell script- what does jot -r 1 1 4 mean? can I replace jot with seq?

I am new to unix and shell script.
I am working on a script to write train and test image file names into two lists:
train.list
test.list
COUNT=-1
for folder in $1/*
do
COUNT=$[$COUNT + 1]
for imagesFolder in "$folder"/*
do
if (( $(jot -r 1 1 $2) > 1 )); then
echo "$imagesFolder" $COUNT >> train.list
else
echo "$imagesFolder" $COUNT >> test.list
fi
done
done
and I get the error:
/convert_images_to_list.sh: line 31: jot: command not found
./convert_images_to_list.sh: line 31: ((: > 1 : syntax error: operand expected (error token is "> 1 ")
I tried to change jot to seq and the error is:
eq: invalid option -- 'r'
./convert_images_to_list.sh: line 31: ((: > 1 : syntax error: operand expected (error token is "> 1 ")
I also checked 'seq --help' which says seq [OPTION]... FIRST INCREMENT LAST.
what does jot -r 1 1 &2 be replaced with, if I want to use seq instead of jot.
Thank you very much.
You probably don't need jot or seq; you are just trying to generate a random integer between 1 and $2. As long as $2 isn't too large and you aren't terribly concerned about using a uniform distribution, you can replace it with
# $RANDOM yields a value between 0 and 32767
# Thus, $RANDOM % $2 yields a value between 0 and $2 - 1
if (( $RANDOM % $2 > 0 )); then
...
jot -r 1 1 $2 produces one random number between 1 and $2. You can't replace it with seq. But you can replace it with shuf -i1-$2 -n1 which Ubuntu should have.
As an alternative to the above answer, I could solve this problem of selecting a portion of image folders into two lists or categories by replacing jot -r 1 1 &2 with a for loop to select from a sequence of numbers.
for imagesFolder in "$folder"/*
do
for t in $(seq 1 1 $2)
do
if (( t > 1 )); then
echo "in train"
echo "$imagesFolder" $COUNT >> train.list
else
echo "in test"
echo "$imagesFolder" $COUNT >> test.list
fi
done
done

For loop structure to add in bash

I'm new to this and what I'm trying to do is create a simple adding script in bash.
I have to use a for loop. What I'm starting so far looks like this:
#!/bin/bash
sum=0
for num in {1..15}
do
echo $num
done
echo$sum
I need help with how to make the for loop show for example if I type:
add 4 -3 2 8
it will output as:
4
-3
2
8
=11
How would I make it so the $num only show what I typed in such as the '4 -3 2 8' and negative numbers?
You can use $# to get all parameters, and $(()) to do arithmetic.
sum=0
for num in $#
do
sum=$((sum + num))
done
echo $# = $sum
I'll retag your question as bash; d is not appropriate.
I'm trying to create an exit error to expand on the previous script now. if I type anything but a number now and what I have tried so far is:
sum=0
for num in "$#"
do
echo $num | grep -i [^0-9+-]
if ["$?" = 1] then
echo "Sorry, '$num' is not a number"
fi
sum=$((sum + num))
done
echo $sum
Example if I type in
add 1 2 3 four five
it would say
four
Sorry, 'four' is not a number

Column Read and Average (BASH)

so I'm dealing with column averaging problems
I have several columns of number,
1 2 3 4 5
2 3 4 5 6
8 4 5 6 7
9 8 7 6 5
1 9 9 9 2
What I want to do is to take an average of each column so that they will yield
4 5 5 6 5
The summing part is not a problem, I'm able to get the sum of each column. However, somehow I'm having trouble in counting the number of columns (variable $x)
Here is my code
while read line; do
x = 0
read -a array <<< "$line"
for i in "${!array[#]}"
do
column[${i}]=$((${column[${i}]}+${array[$i]}))
((x++))
done
done < $TMP
for sum in ${column[#]}
do
average=`expr $sum / $x`
remainder=`expr $sum % $x`
mult=`expr $remainder \* 10`
fracvalue=`expr $mult / $x`
echo $x
echo $average
echo $sum
echo $remainder
echo $mult
echo $fracvalue
done
The last several lines are for my testing purposes, here the $X keeps showing 1 instead of 5. That's why it screws up all the other variable. Does anybody know where's the flaw in this code? Really appreciate your help. Thanks
Another approach:
#!/bin/bash
declare -ia sum # declare array of integers
declare -i lines=0 # declare integer to count lines
# read file to array and sum values
while read -ra array; do
for ((i=0; i<${#array[#]};i++)); do
sum[$i]+=${array[$i]}
done
lines+=1
done < file
# print averages
for ((j=0; j<$i;j++)); do
echo -n "$((${sum[$j]}/$lines)) "
done
Output:
4 5 5 6 5
The problem is in the line
x = 0
should be
x=0 and make ((x+1)) as let "x+=1" it should work.
Maybe your file doesn't have the same number of lines in each column.
while read line; do
read -a array <<< "$line"
for i in "${!array[#]}"
do
column[${i}]=$((${column[${i}]}+${array[$i]}))
((x[${i}]++))
done
done < $TMP
for i in ${!column[#]}
do
sum=${column[$i]}
x=${x[${i}]}
#your calcul
done
The initial issue, aside from a few syntax peculiarities, was the resetting of x on each read which caused you to lose your line count. Additionally, your array is an indexed array, not an associative array.
Making the small adjustments and declareing the variables at the beginning (to hint to bash they are integers or arrays), it works as planned. note: there is no need for a here-string to assign the array, just use regular array syntax. Also note, for indexed arrays, there is no need to dereference the variable within [ ], simply ${array[i]} is fine.
While the use of expr is fine (old, slow, but portable and fine), you could use bash arithmetic syntax for your parameter calculations at the end. (you already have arrays). Finally, you are better served initializing each column[$i]=0 on the first read loop. You can do that with a simple flag:
#!/bin/bash
declare -i ncols=0
declare -i x=0
declare -a column
while read -r line; do
array=( $line )
[ $ncols -eq 0 ] && ncols=${#array[#]}
for ((i = 0; i < ncols; i++))
do
[ $x -eq 0 ] && column[$i]=0
column[$i]=$(( ${column[i]} + ${array[i]}))
done
((x++))
done < "$1"
for sum in ${column[#]}
do
average=`expr $sum / $x`
remainder=`expr $sum % $x`
mult=`expr $remainder \* 10`
fracvalue=`expr $mult / $x`
echo $x
echo $average
echo $sum
echo $remainder
echo $mult
echo $fracvalue
done
exit 0
Output
$ bash colavg.sh dat/colavg.txt
5
4
21
1
10
2
5
5
26
1
10
2
5
5
28
3
30
6
5
6
30
0
0
0
5
5
25
0
0
0

Bash Script accepting a number, then printing a set of int from 0 to the number entered

I am trying to write a bash script that accepts a number from the keyboard, and then prints a set of integers from 0 to the number entered. I can't figure out how to do it at all.
This is my code:
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( $user_answer = $user_answer; $user_answer>0; $user_answer--))
echo $user_answer
fi
done
exit
The error I'm recieving is:
number_loop: line 10: syntax error near unexpected token echo'
number_loop: line 10: echo $user_answer'
Assign a separate variable in order to use increment/decrement operators. $user_answer=$user_answer will always be true and it will throw an error when trying to use decrement. Try the following :
#!/bin/bash
while [ 1 ]
do
echo -n "Enter a color: "
read user_answer
for (( i=$user_answer; i>0; i-- ))
do
echo $i
done
done
exit
You missed the do statement between your for and the echo.
bash has many options to write numbers. What you seem to be trying to do is easiest done with seq:
seq $user_answer -1 0
If you want to use your loop, you have to insert a ; do and replace the fi with done, and replace several $user_answer:
for (( i = $user_answer; i>0; i--)); do
echo $i
done
(btw: I assumed that you wanted to write the numbers in reverse order, as you are going backwards in your loop. Forwards is even easier with seq:
seq 0 $user_input
)
This is where a c-style loop works particularly well:
#!/bin/bash
for ((i = 1; i <= $1; i++)); do
printf "%s\n" "$i"
done
exit 0
Example
$ bash simplefor.sh 10
1
2
3
4
5
6
7
8
9
10
Note: <= is used as the for loop test so it 10 it iterates 1-10 instead of 0-9.
In your particular case, iterating from $user_answer you would want:
for (( i = $user_answer; i > 0; i--)); do
echo $i
done
The for loop is a bash internal command, so it doesn't fork a new process.
The seq command has a nice, one-line syntax.
To get the best of the twos, you can use the { .. } syntax:
eval echo {1..$answer}

Resources