Modulus function in bash shell script - linux

for ((i=0; i<lenPT; i++)) do
if [[ $(($lenPT % 2)) == 0]] then
P[i] = "$((K [0] * arrT[i] + K[2] * arrT[i+1]))"
else
P[i] = "$((K[1]*arrT[i-1]+K[3]*arrT[i]))"
fi
done
I got errors saying that "syntax error in conditional expression" and "syntax error near 'then'". What is the error in my conditional statement?

Space matters, see Barmar's answer. You also need a semicolon after the [[ ]] conditional if you want to put then on the same line.
Instead of the cumbersome [[ $(( )) ... ]] combination, you can use the (Bash-only) (( )) conditional, the contents of which are evaluated in an arithmetic context:
if ((lenPT % 2 == 0)); then
You don't even need $lenPT in this construct, lenPT is enough (see Conditional Constructs in the manual for details).
Since the exit status of ((...)) is 1 (not successful) if the expression evaluates to 0, and 0 (successful) otherwise, you could swap the branches and shorten the condition a little:
if ((lenPT % 2)); then
P[i]=$((K[1] * arrT[i-1] + K[3] * arrT[i]))
else
P[i]=$((K[0] * arrT[i] + K[2] * arrT[i+1]))
fi

You need a space before ]].
if [[ $(($lenPT % 2)) == 0 ]]; then

The if ... else that depends on the value of $lenPT is needless, since $lenPT never changes within the loop. The assignments are so similar the if logic can be replaced with arithmetic. Example:
n=$((lenPT % 2))
for ((i=0; i<lenPT; i++))
do
P[i]="$((K[n] * arrT[i-n] + K[2+n] * arrT[i+1-n]))"
done

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 Scripting Ternary operator to get string result

In shell scripting, I am using ternary operator like this:
(( numVar == numVal ? (resVar=1) : (resVar=0) ))
I watched shell scripting tutorial by Derek Banas and got the above syntax at 41:00 of the video
https://www.youtube.com/watch?v=hwrnmQumtPw&t=73s
The above code works when we assign numbers to resVar, but if I try to assign a string to resVar, it always returns 0.
(( numVar == numVal ? (resVar="Yop") : (resVar="Nop") ))
and also tried
resVar=$(( numVar == numVal ? (echo "Yop") : (echo "Nop") ))
So which is the right way to do this?
You didn't tell us what shell you use but it's possible you use
bash or something similar. Ternary operator in Bash works only with numbers as
explained in man bash under ARITHMETIC EVALUATION section:
The shell allows arithmetic expressions to be evaluated, under
certain circumstances (see the let and declare builtin commands and
Arithmetic Expansion). Evaluation is done in fixed-width integers
with no check for over- flow, though division by 0 is trapped and
flagged as an error. The operators and their precedence,
associativity, and values are the same as in the C language. The
following list of operators is grouped into levels of
equal-precedence operators. The levels are listed in order of
decreasing precedence.
(...)
expr?expr:expr
conditional operator
And the reason that resVar is assigned 0 when you use "Yop" or
"Nop" is because such string is not a valid number in bash and
therefore it's evaluated to 0. It's also explained in man bash in
the same paragraph:
A null value evaluates to 0.
It's also explained in this Wikipedia
article if you find it
easier to read:
A true ternary operator only exists for arithmetic expressions:
((result = condition ? value_if_true : value_if_false))
For strings there only exist workarounds, like e.g.:
result=$([ "$a" == "$b" ] && echo "value_if_true" || echo
"value_if_false")
(where "$a" == "$b" can be any condition test, respective [, can
evaluate.)
Arkadiusz already pointed out that ternary operators are an arithmetic feature in bash, not usable in strings. If you want this kind of functionality in strings, you can always use arrays:
$ arr=(Nop Yop)
$ declare -p arr
declare -a arr='([0]="Nop" [1]="Yop")'
$ numVar=5; numVal=5; resvar="${arr[$((numVar == numVal ? 1 : 0))]}"; echo "$resvar"
Yop
$ numVar=2; numVal=5; resvar="${arr[$((numVar == numVal ? 1 : 0))]}"; echo "$resvar"
Nop
Of course, if you're just dealing with two values that can be in position 0 and 1 in your array, you don't need the ternary; the following achieves the same thing:
$ resvar="${arr[$((numVar==numVal))]}"
you can use this simple expression :
resVar=$([ numVar == numVal ] && echo "Yop" || echo "Nop")
Running with Gohti's idea to make the script more readable:
#!/bin/bash
declare -a resp='([0]="not safe" [1]="safe")'
temp=70; ok=$(( $temp > 60 ? 1 : 0 ))
printf "The temperature is $temp Fahrenheit, it is ${resp[$ok]} to go outside\n";
temp=20; ok=$(( $temp > 60 ? 1 : 0 ))
printf "The temperature is $temp Fahrenheit, it is ${resp[$ok]} to go outside\n";

Bash interpreting increment operation as command

I'm trying to batch modify some images using a bash script, and to print out the progress. It looks to me like bash is interpreting the increment to counter as a command and is giving the following error:
augment_data.sh: line 20: 0: command not found
Here is the code:
for file in *.jpg
do
convert $file -rotate 90 rotated_"$file"
((counter++))
if $((counter % 10 == 0)); then
echo "Rotated $counter files out of $num_files"
fi
done
with line 20 being the one with the counter increment operation.
How can I fix this so that I don't get the error message?
In an arithmetic substitution, the result of an arithmetic operation is substituted in the position of the operation itself.
In this case, $(( 1 == 0 )) has an arithmetic result of 0, and $(( 1 == 1 )) has a result of 1.
Thus, if you use $(( ... )), then this 0 or 1 is substituted in that position, and so gets run as a command. Since you don't have commands named 0 or 1 (probably), either of these will result in a command not found error.
If you use (( ... )), then the arithmetic result directly sets return value, but no expansion takes place.

if: Expression syntax Error in csh - not to accept other string than the only string it must accept

There's this daemon with, for ex. 5 types in one script. Now, i want to be able to start/stop it by specifying the number of the daemon(to start one by one), OR specify "all" (to start in bulk).
The format: (runscript) (commandName) (daemon # or "all")
Need to satisfy two conditions, when the user inputs: (1) correctly (either by number or "all) OR
(2) incorrectly (either inputted num is greater than $count or all other string than "all").
All conditions are already achieved except for one, if the user inputs other string than "all"
Sample code:
case 'startDaemon': #commandName
set count = 5
if ($#argv == 2 && $2 == all) then
echo "correct, do this"
else if ($#argv == 2 && $2 < $count) then
echo "correct too, do this"
else if ($#argv == 2 && ($2 != all || $2 >= $count)) then
echo "Incorrect parameter: specify daemon # less than $count or 'all' to start all."
else
echo "Please use: $0(runscript) $1(commandname) (daemon # or all)"
whenever I type: (runscript) startDaemon hello, for example, error shows:
if: Expression syntax
When it should have gone to the 3rd condition. Please help and kindly point out if the prob is in the conditions or logical operators or whatever. Thanks in advance
PS. Im using csh. The script given to me is in csh, so yep.
The immediate problem is the comparison $2 < $count, which is invalid when $count contains a string.
Here is a working solution:
#!/bin/csh
set count = 5
if ($#argv == 2) then
if ($2 == all) then
echo "correct, do this"
else if (`echo $2 | grep '^[0-9]*$'`) then
if ($2 < $count) then
echo "correct too, do this"
else
echo "Number must be less than 5"
endif
else
echo "Incorrect parameter: specify daemon # less than $count or 'all' to start all."
endif
else
echo "Please use: $0(runscript) $1(commandname) (daemon # or all)"
endif

Problem with bash code

function dec_to_bin {
if [ $# != 2 ]
then
return -1
else
declare -a ARRAY[30]
declare -i INDEX=0
declare -i TEMP=$2
declare -i TEMP2=0
while [ $TEMP -gt 0 ]
do
TEMP2="$TEMP%2"
#printf "%d" "$TEMP2"
ARRAY[$INDEX]=$TEMP2
TEMP=$TEMP/2
INDEX=$[ $INDEX + 1 ] #note
done
for (( COUNT=INDEX; COUNT>-1; COUNT--)){
printf "%d" "${ARRAY[$COUNT]}" <<LINE 27
#echo -n ${ARRAY[$COUNT]} <<LINE 28
}
fi
}
why is this code giving this error
q5.sh: line 27: ARRAY[$COUNT]: unbound variable
same error comes with line 28 if uncommented
One more question, I am confused with the difference b/w '' and "" used in bash scripting any link to some nice article will be helpfull.
It works fine for me except that you can't do return -1. The usual error value is 1.
The error message is because you have set -u and you're starting your for loop at INDEX instead of INDEX-1 (${ARRAY[INDEX]} will always be empty because of the way your while loop is written). Since you're using %d in your printf statement, empty variables will print as "0" (if set -u is not in effect).
Also, it's meaningless to declare an array with a size. Arrays in Bash are completely dynamic.
I would code the for loop with a test for 0 (because the -1 looks confusing since it can't be the index of an numerically indexed array):
for (( COUNT=INDEX - 1; COUNT>=0; COUNT--))
This form is deprecated:
INDEX=$[ $INDEX + 1 ]
Use this instead:
INDEX=$(( $INDEX + 1 ))
or this:
((INDEX++))
I also recommend using lower case or mixed case variables as a habit to reduce the chance of variable name collision with shell variables.
You're not using $1 for anything.

Resources