I'm getting different exit codes when using catch to execute git checkout main
in Tcl compared to running it directly in the shell. In Tcl, when I do % catch "exec git checkout main" result
and % catch {exec git checkout main} result
, both return 1 as the exit code. But when I run $git checkout main
in the shell, and then $echo $?
, it returns 0. Also, in Tcl, % puts $result
shows "Already on 'main'"
. Why is there this difference in exit codes and how can I get consistent behavior?
Tcl:
% catch "exec git checkout main" result
1
% catch {exec git checkout main} result
1
% puts $result
Already on 'main'
% exit
tcsh:
$git checkout main
Already on 'main'
$echo $?
0
The Tcl catch
command does not return the exit code of the subprocess you invoke using the exec
command. It returns the status of the exec
command, which is something different.
The exec
command returns an error (code 1) if any of the commands in the pipeline exit abnormally or are killed or suspended. If any of the commands writes to its standard error file and that standard error is not redirected and the -ignorestderr
option is not specified, then exec
also returns an error. In your case exec
returns an error because git
reported "Already on 'main'" on standard error.
To check the exit code of the subprocess executed by exec
, you have to look at the errorCode
variable, or the -errorcode
return option:
catch {exec git checkout main} result options
puts [dict get $options -errorcode]
When the exit code is 0, this will print "NONE". Otherwise it prints something like "CHILDSTATUS 7230 1", where 7230 is the PID of the subprocess and 1 is the exit code.
Alternatively, you can act on the error code prefix using the try
command:
try {
exec git checkout main
} trap {CHILDSTATUS} {message options} {
puts "Exit code: [lindex [dict get $options -errorcode] 2]"
} trap {NONE} {
# Process wrote to stderr - ignore
}
To just get a simple pass/fail indication from catch
, tell exec
to ignore standard error in one of two ways:
catch {exec git checkout main 2>@1} result
Or
catch {exec -ignorestderr git checkout main} result
The second method will result in any standard error output of the pipeline to be reported on the standard output of your Tcl program.