Why do these 2 methods result in different outcomes?

I've been troubleshooting a ksh93 script I wrote today and have narrowed it down to the root cause. but I don't understand why?

so, i've written the following script to demonstrate the problem i found today:

#!/bin/ksh

method=$1

FILE_LIST=( $(find /someplace -type f -printf "%T@\t%p\n" 2>/dev/null | sort -r | awk '{print $2}') )

print "found ${#FILE_LIST[*]} files."

typeset -li counter=0
for file in ${FILE_LIST[*]}
do
    case $method in
	0)
	    [[ -f $file ]] \
	        && (( counter++ )) \
	        && continue
	    ;;
	1)
	    if [[ -f "$file" ]] ; then
		(( counter++ ))
		continue
	    fi
	    ;;
	*)
	    print "don't know that method"
	    exit 1
	    ;;
    esac

    print "should never get here: $file"
done

print "counter=$counter"

the code for when method=0 or 1 provides different results. I *thought* it would be the same, and I don't understand why it isn't:

$ ./t60.ksh 1
found 106 files.
counter=106
$ ./t60.ksh 0
found 106 files.
should never get here: /someplace/.t60.ksh.swp
counter=106

The ".t60.ksh.swp" file is simply the 1st element of the FILE_LIST array. Why does method=0 result in execution of the "should never get here" line once? (and why only once?) I was expecting it to have the same result as method=1.

After running ((counter++)), the exit code is 1, not zero, and hence the continue statement is not executed. The reason is that the value of counter++ is what is stored in counter BEFORE it is incremented, i.e zero. If the value of the expression is zero, the exit code of the ((....)) command is 1. If the value of the expression is not zero, the exit code of the ((....)) command is 0.

The AND operator && in the form of command1 && command2 says that command2 is executed only if command1 returns an exit status of zero. (( counter++ )) will not return zero so continue is not hit.
With method 1 the continue will be executed unconditionally.

To complement rovf correct explanation ..
To have expected result, altho a lot of redundant code is there, increment the variable BEFORE
++counter , instead of AFTER counter++

Or just leave out the ((...)) syntax and use counter=$((counter+1)) , which is to be used if you do not require behaviour ((...)) offers.

Regards
Peasant.

thank you everyone. i got it now. i was confused about the return behavior of (( counter++ )). so, to get what i was expecting, i can use either:

(( ++counter ))
(( counter = counter + 1 ))
counter=$((counter+1))
let counter=counter+1

all of the above would return 0. where as:

(( counter++ ))

would return 1 (when counter=0), but return 0 after counter > 0.

This behaviour is explained in the following URL/book
Numeric Variables and Arithmetic (Learning the Korn Shell, 2nd Edition)

Under 6.2.2. Arithmetic Conditionals

It's a good read if you wish to maintain and program in ksh.

Regards
Peasant.

Not quite. All of

(( ++counter ))
(( counter = counter + 1 ))
let counter=counter+1

return 1 when run with counter=-1 .

1 Like

But the standard way of increment without evaluating will work as you expect always.

+ counter=-1
+ typeset -li counter
counter=$(( counter + 1 ))
+ counter=0
echo $counter $?
+ echo 0 0
0 0

thanks people for helping me understand the subtleties. I think for the sake of clarity, I'm just going to use something like:

if [[ -f "$file" ]] ; then
	(( counter++ ))
	continue
fi

For the sake of clarity, *I* would write it like this:

if [[ -f "$file" ]]
then
  (( counter++ ))
elif [[ -e $file ]]
then
  echo "$file is not a plain file, but $(file $file)"
else
  echo "$file disappeared"
fi

This message is probably clearer than "should never get here" :wink:

For the sake of clarity i suggest you stay away from keywords like "continue" and "break" as long as possible and use them only as a very last resort. You see, both are basically variants of the GOTO and we know how we feel about GOTO, don't we?

In your example code i don't even understand what the "continue" should accomplish: the case-part already finishes at this point and the next iteration of the for-loop will start anyway, so the difference is perhaps the time it takes to execute the "fi", the ";;" and the "next" - is it only me or should that really be negligible?

I hope this helps.

bakunin