Reputation: 117
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
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
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