user3567004
user3567004

Reputation: 117

Tcsh Script Last Exit Code ($?) value is resetting

I am running the following script using tcsh. In my while loop, I'm running a C++ program that I created and will return a different exit code depending on certain things. While it returns an exit code of 0, I want the script to increment counter and run the program again.

#!/bin/tcsh

echo "Starting the script."
set counter = 0

while ($? == 0)
    @ counter ++
    ./auto $counter
end

I have verified that my program is definitely returning with exit code = 1 after a certain point. However, the condition in the while loop keeps evaluating to true for some reason and running.

I found that if I stick the following line at the end of my loop and then replace the condition check in the while loop with this new variable, it works fine.

while ($return_code == 0)
    @ counter ++
    ./auto $counter
    set return_code = $?
end

Why is it that I can't just use $? directly? Is another operation underneath the hood performed in between running my custom program and checking the loop condition that's causing $? to change value?

Upvotes: 1

Views: 4102

Answers (2)

dash-o
dash-o

Reputation: 14493

Slightly shorter/simpler explanation:

Recall that with tcsh/csh EACH command (including shell builtin) return a status. Therefore $? (aliases to $status) is updated by 'if' statements, 'for' loops, assignments, ...

From practical point of view, better to limit the usage of direct use of $? to an if statement after the command execution:

do-something
if ( $status == 0 )
   ...
endif

In all other cases, capture the status in a variable, and use only that variable

do-something
something_status=$?
if ( $something_status == 0 )
   ...
endif

To expand on the $status, even a condition test in an if statement will modify the status, therefore the following repeated test on $status will not never hit the '$status == 5', even when do-something will return status of 5

do-something
if ( $status == 2 ) then
  echo FOO
else if ( $status == 5 ) then
  echo BAR
endif

Upvotes: 2

Keith Thompson
Keith Thompson

Reputation: 263537

That is peculiar.

I've altered your example to something that I think illustrates the issue more clearly. (Note that $? is an alias for $status.)

#!/bin/tcsh -f

foreach i (1 2 3)
    false
    # echo false status=$status
end
echo Done status=$status

The output is

Done status=0

If I uncomment the echo command in the loop, the output is:

false status=1
false status=1
false status=1
Done status=0

(Of course the echo in the loop would break the logic anyway, because the echo command completes successfully and sets $status to zero.)

I think what's happening is that the end that terminates the loop is executed as a statement, and it sets $status ($?) to 0.

I see the same behavior with both tcsh and bsd-csh.

Saving the value of $status in another variable immediately after the command is a good workaround -- and arguably just a better way of doing it, since $status is extremely fragile, and will almost literally be clobbered if you look at it.

Note that I've add a -f option to the #! line. This prevents tcsh from sourcing your init file(s) (.cshrc or .tcshrc) and is considered good practice. (That's not the case for sh/bash/ksh/zsh, which assign a completely different meaning to -f.)

A digression: I used tcsh regularly for many years, both as my interactive login shell and for scripting. I would not have anticipated that end would set $status. This is not the first time I've had to find out how tcsh or csh behaves by trial and error and been surprised by the result. It is one of the reasons I switched to bash for interactive and scripting use. I won't tell you to do the same, but you might want to read Tom Christiansen's classic "csh.whynot".

Upvotes: 1

Related Questions