Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

child process exited abnormally

1,537 views
Skip to first unread message

Cecil Westerhof

unread,
Aug 15, 2018, 3:28:05 PM8/15/18
to
I want to display the status of a systemd service. For this I use:
proc giveStatus {service} {
catch {exec systemctl status ${service}} status
puts "\nStatus:\n${status}"
}

But when the service is not running I get beside the status also:
child process exited abnormally

Why is this and what can I do about that?

--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof

Rich

unread,
Aug 15, 2018, 4:38:03 PM8/15/18
to
Cecil Westerhof <Ce...@decebal.nl> wrote:
> I want to display the status of a systemd service. For this I use:
> proc giveStatus {service} {
> catch {exec systemctl status ${service}} status
> puts "\nStatus:\n${status}"
> }
>
> But when the service is not running I get beside the status also:
> child process exited abnormally
>
> Why is this and what can I do about that?

That is output by tcl when the process returns a non-zero return code
(meaning error):

$ rlwrap tclsh
% exec true
% exec false
child process exited abnormally
%

Your catch is catching it, but you are then outputting it in your
followon puts:

% catch {exec false} e1 e2
1
% puts $e1
child process exited abnormally
%

It is what Tcl puts into the "status" variable on the catch command
line.

Cecil Westerhof

unread,
Aug 16, 2018, 3:59:06 AM8/16/18
to
That cannot be circumvented?

In Bash I did:
systemctl status "${service}" || true

How would I do the same in Tcl? I tried several things, but none
worked. For example:
set status [exec systemctl {*}"status ${service} || true"]
gives:
Unit \x7c\x7c.service could not be found.
Unit true.service could not be found.

Ralf Fassel

unread,
Aug 16, 2018, 4:47:27 AM8/16/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| That cannot be circumvented?

No, but it is not a problem.

| In Bash I did:
| systemctl status "${service}" || true
>
| How would I do the same in Tcl? I tried several things, but none
| worked. For example:
| set status [exec systemctl {*}"status ${service} || true"]
| gives:
| Unit \x7c\x7c.service could not be found.
| Unit true.service could not be found.

You need to understand how exec works.

Man exec(n)

[...]
If any of the commands in the pipeline exit abnormally or are killed
or suspended, then exec will return an error
[...]
If any of the commands writes to its standard error file and that
standard error is not redirected and -ignorestderr is not specified,
then exec will return an error

Thus if you don't [catch] the exec, the error will stop your script.

If the [catch] triggers, global variable errorCode (or the -errorcode
key in the optional options dict) holds more information about the cause
of the failure:

% catch {exec systemctl status foobar} output opts
1
% set output
● foobar.service
Loaded: not-found (Reason: No such file or directory)
Active: inactive (dead)
child process exited abnormally

% set opts
-code 1 -level 0 -errorcode {CHILDSTATUS 23458 3} -errorinfo {● foobar.service
Loaded: not-found (Reason: No such file or directory)
Active: inactive (dead)
child process exited abnormally
while executing
"exec systemctl status foobar"} -errorline 1

% set ::errorCode
CHILDSTATUS 23458 3

Note also the following section in exec(n):

WORKING WITH NON-ZERO RESULTS
To execute a program that can return a non-zero result, you
should wrap the call to exec in catch and check the contents of
the -errorcode return option if you have an error:

set status 0
if {[catch {exec grep foo bar.txt} results options]} {
set details [dict get $options -errorcode]
if {[lindex $details 0] eq "CHILDSTATUS"} {
set status [lindex $details 2]
} else {
# Some other error; regenerate it to let caller handle
return -options $options -level 0 $results
}
}


HTH
R'

Rich

unread,
Aug 16, 2018, 6:04:59 AM8/16/18
to
No, it is Tcl telling you the reason why the 'exec' returned an error
(non-zero return codes from CLI programs conventionally indicate
"not-successful" statuses.

As for not seeing the string printed to the console, how about not
blindly doing the puts here: puts "\nStatus:\n${status}"

That line is why you see the string being printed, it executes on
success or on failure. And you need to read the catch manpage more
carefully. Specifically these two sentences, which are critical:

If the resultVarName argument is given, then the variable it names
is set to the result of the script evaluation. When the return
code from the script is 1 (TCL_ERROR), the value stored in
resultVarName is an error message.

The "resultvar" parameter to catch is one of:
1) the command result *or*
2) the error message
depending upon whether catch returns success or error.

Your code treats it as if it were "only the result" -- but it is not
"only the result".

Cecil Westerhof

unread,
Aug 16, 2018, 6:44:05 AM8/16/18
to
That is the problem: when a service is not running 'systemctl status'
gives back 3. At the moment I solved my problem with:
proc getCleanOutput {command} {
set errorStr "\nchild process exited abnormally"
set length [string length ${errorStr}]
catch {exec {*}${command}} status
if {[regexp "${errorStr}\$" ${status}]} {
set status [string range ${status} 0 end-${length}]
}
return ${status}
}

proc giveStatus {service} {
set status [getCleanOutput "systemctl status ${service}"]
puts "\nStatus:\n${status}"
}

But with your code I could improve it. One improvement would be a list
of codes that are acceptable, so another code would still be an error.

Cecil Westerhof

unread,
Aug 16, 2018, 7:28:05 AM8/16/18
to
Well, that is not correct. It returns the command result AND the error
message. (When there is no error that is only the command result.)

But I kind of found a way around it. (See my other reply.)

Rich

unread,
Aug 16, 2018, 7:47:58 AM8/16/18
to
Yes, but you are also throwing away part of the result of catch, the
return code from catch, which would tell you whether you got a success
or non-success error code:

Your code:

catch {exec ...} abc

This throws away catch's return code (which is a boolean).

Your code should instead be something like this:

set r [catch {exec ...} abc]
if {$r} {
# catch returned an error, do appropriate stuff, abc contains Tcl's
# indication of type of error (plus anything returned on stdout by
# the exec'ed command)
} else {
# catch did not return an error, 'abc' contains stdout of the
# exec'ed command
}

Now, we grab the boolean that catch returns, and use it to decide what
to do with 'abc'.

Cecil Westerhof

unread,
Aug 16, 2018, 8:14:05 AM8/16/18
to
I did something like that. I post it as a reply on the reply where I
posted my 'cleaning' proc.

Cecil Westerhof

unread,
Aug 16, 2018, 8:14:05 AM8/16/18
to
I improved the getCleanOutput:
proc getCleanOutput {command} {
if {[catch {exec {*}${command}} status]} {
set errorStr "\nchild process exited abnormally"
set length [string length ${errorStr}]
switch [lindex ${::errorCode} 2] {
3 {
if {[regexp "${errorStr}\$" ${status}]} {
set status [string range ${status} 0 end-${length}]
} else {
puts stderr "getCleanOutput: Did not find errorStr"
}
}
default {
error "'${command}' returned ${retCode}\n${status}\n"
}
}
}
return ${status}
}


> But with your code I could improve it. One improvement would be a list
> of codes that are acceptable, so another code would still be an error.

That is something for another time. ;-)

Ralf Fassel

unread,
Aug 16, 2018, 10:12:18 AM8/16/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| That is the problem: when a service is not running 'systemctl status'
| gives back 3. At the moment I solved my problem with:
--<snip-snip>--
| But with your code I could improve it. One improvement would be a list
| of codes that are acceptable, so another code would still be an error.

In addition to Richs suggestion (using return code of catch to
determiene whether there was an error at all), instead of regexp
matching the returned string you should examine ::errorCode (cf
errorCode(n)).

The first element clearly tells you what happened:

Man errorCode(n)
errorCode
This variable holds the value of the -errorcode return
option set by the most recent error that occurred in this
interpreter. This list value represents additional
information about the error in a form that is easy to
process with programs. The first element of the list
identifies a general class of errors, and determines the
format of the rest of the list.
[...]

Thus:
catch {exec false}
set ::errorCode
=> CHILDSTATUS 28044 1

So general processing would be:
if catch ...
switch -- [lindex $::errorCode 0] {
CHILDSTATUS {
# CHILDSTATUS pid code
# This format is used when a child process has exited with a
# non-zero exit status. [...]
}
...


HTH
R'

Cecil Westerhof

unread,
Aug 16, 2018, 11:59:05 AM8/16/18
to
Ralf Fassel <ral...@gmx.de> writes:

> * Cecil Westerhof <Ce...@decebal.nl>
> | That is the problem: when a service is not running 'systemctl status'
> | gives back 3. At the moment I solved my problem with:
> --<snip-snip>--
> | But with your code I could improve it. One improvement would be a list
> | of codes that are acceptable, so another code would still be an error.
>
> In addition to Richs suggestion (using return code of catch to
> determiene whether there was an error at all), instead of regexp
> matching the returned string you should examine ::errorCode (cf
> errorCode(n)).

I do that now. By the way: the string is not always added.


> The first element clearly tells you what happened:
>
> Man errorCode(n)
> errorCode
> This variable holds the value of the -errorcode return
> option set by the most recent error that occurred in this
> interpreter. This list value represents additional
> information about the error in a form that is easy to
> process with programs. The first element of the list
> identifies a general class of errors, and determines the
> format of the rest of the list.
> [...]
>
> Thus:
> catch {exec false}
> set ::errorCode
> => CHILDSTATUS 28044 1
>
> So general processing would be:
> if catch ...
> switch -- [lindex $::errorCode 0] {
> CHILDSTATUS {
> # CHILDSTATUS pid code
> # This format is used when a child process has exited with a
> # non-zero exit status. [...]
> }
> ...

I use:
if {[lindex ${::errorCode} 2] == 3} {

After an exec I expect the first element to be always CHILDSTATUS, or
is that a wrong expectation.

A status 3 is not an error (only means the service is not running), so
in that case I accept the output with the error message removed if it
is there. When there is another status I throw an error.


Learned a few new things today and I keep finding it nice to see how
easy it is to write code with Tcl. :-D

Cecil Westerhof

unread,
Aug 16, 2018, 3:59:05 PM8/16/18
to
Cecil Westerhof <Ce...@decebal.nl> writes:

> I improved the getCleanOutput:
> proc getCleanOutput {command} {
> if {[catch {exec {*}${command}} status]} {
> set errorStr "\nchild process exited abnormally"
> set length [string length ${errorStr}]
> switch [lindex ${::errorCode} 2] {
> 3 {
> if {[regexp "${errorStr}\$" ${status}]} {
> set status [string range ${status} 0 end-${length}]
> } else {
> puts stderr "getCleanOutput: Did not find errorStr"
> }
> }
> default {
> error "'${command}' returned ${retCode}\n${status}\n"
> }
> }
> }
> return ${status}
> }
>
>
>> But with your code I could improve it. One improvement would be a list
>> of codes that are acceptable, so another code would still be an error.
>
> That is something for another time. ;-)

Implemented:
proc getCleanOutput {command nonerrors} {
if {[catch {exec {*}${command}} status]} {
set errorStr "\nchild process exited abnormally"
set length [string length ${errorStr}]
set retCode [lindex ${::errorCode} 2]
if {[regexp "${errorStr}\$" ${status}]} {
set status [string range ${status} 0 end-${length}]
}
if {! (${retCode} in ${nonerrors})} {
error "'${command}' returned ${retCode}\n${status}\n"
}
}
return ${status}
}

Ralf Fassel

unread,
Aug 17, 2018, 5:13:44 AM8/17/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| > So general processing would be:
| > if catch ...
| > switch -- [lindex $::errorCode 0] {
| > CHILDSTATUS {
| > # CHILDSTATUS pid code
| > # This format is used when a child process has exited with a
| > # non-zero exit status. [...]
| > }
| > ...
>
| I use:
| if {[lindex ${::errorCode} 2] == 3} {
>
| After an exec I expect the first element to be always CHILDSTATUS, or
| is that a wrong expectation.

It's a wrong expectation. There's a zillion things that can go wrong,
each with their own error code. Check the errorcode manpage for some
possible values:

https://www.tcl.tk/man/tcl/TclCmd/tclvars.htm#M12

| A status 3 is not an error (only means the service is not running), so
| in that case I accept the output with the error message removed if it
| is there.

That's ok only if the first parameter in errorCode is CHILDSTATUS.
Any other error could by chance have a 3 in there, too with a completely
different meaning (yes, I know, your approach will work 99% of the times).

| Learned a few new things today and I keep finding it nice to see how
| easy it is to write code with Tcl. :-D

Me 2 :-)

R'

Cecil Westerhof

unread,
Aug 17, 2018, 7:44:05 AM8/17/18
to
Ralf Fassel <ral...@gmx.de> writes:

> * Cecil Westerhof <Ce...@decebal.nl>
> | > So general processing would be:
> | > if catch ...
> | > switch -- [lindex $::errorCode 0] {
> | > CHILDSTATUS {
> | > # CHILDSTATUS pid code
> | > # This format is used when a child process has exited with a
> | > # non-zero exit status. [...]
> | > }
> | > ...
>>
> | I use:
> | if {[lindex ${::errorCode} 2] == 3} {
>>
> | After an exec I expect the first element to be always CHILDSTATUS, or
> | is that a wrong expectation.
>
> It's a wrong expectation. There's a zillion things that can go wrong,
> each with their own error code. Check the errorcode manpage for some
> possible values:
>
> https://www.tcl.tk/man/tcl/TclCmd/tclvars.htm#M12

OK, thanks. I will implement that.


> | A status 3 is not an error (only means the service is not running), so
> | in that case I accept the output with the error message removed if it
> | is there.
>
> That's ok only if the first parameter in errorCode is CHILDSTATUS.
> Any other error could by chance have a 3 in there, too with a completely
> different meaning (yes, I know, your approach will work 99% of the times).

99% is not good enough. ;-)

Ralf Fassel

unread,
Aug 17, 2018, 11:04:27 AM8/17/18
to
* Cecil Westerhof <Ce...@decebal.nl>
| Ralf Fassel <ral...@gmx.de> writes:
| > * Cecil Westerhof <Ce...@decebal.nl>
| > | After an exec I expect the first element to be always CHILDSTATUS, or
| > | is that a wrong expectation.
| >
| > It's a wrong expectation. There's a zillion things that can go wrong,
| > each with their own error code. Check the errorcode manpage for some
| > possible values:
| >
| > https://www.tcl.tk/man/tcl/TclCmd/tclvars.htm#M12
>
| OK, thanks. I will implement that.

One notable case is when the external program exits successfully, but
prints to stderr:

% catch {exec sh -c {echo foo >&2}} err opts
1

% set errorCode
NONE

% set err
foo

% set opts
-code 1 -level 0 -errorcode NONE -errorinfo {foo
while executing
"exec sh -c {echo foo >&2}"} -errorline 1

'NONE' in this context does not mean there was no error, but that there
is no more info available about the error. Again, see above reference.

| > That's ok only if the first parameter in errorCode is CHILDSTATUS.
| > Any other error could by chance have a 3 in there, too with a completely
| > different meaning (yes, I know, your approach will work 99% of the times).
>
| 99% is not good enough. ;-)

:-)

R'

Schelte Bron

unread,
Aug 17, 2018, 5:41:56 PM8/17/18
to
Cecil Westerhof wrote:
> I use:
> if {[lindex ${::errorCode} 2] == 3} {
>
> After an exec I expect the first element to be always CHILDSTATUS,
> or is that a wrong expectation.
>
If you want to catch specific error codes, it is much easier to use
the try command:

try {
exec bash -c {exit 3}
} trap CHILDSTATUS {- opts} {
# Command produced a non-zero exit code
puts "Exit code: [lindex [dict get $opts -errorcode] 2]"
}

This way any other errors are still thrown as usual. Of course, if
you want to handle those too, you can simply add an "on error"
clause.


Schelte.

Cecil Westerhof

unread,
Aug 17, 2018, 7:28:05 PM8/17/18
to
Ralf Fassel <ral...@gmx.de> writes:

> * Cecil Westerhof <Ce...@decebal.nl>
> | Ralf Fassel <ral...@gmx.de> writes:
> | > * Cecil Westerhof <Ce...@decebal.nl>
> | > | After an exec I expect the first element to be always CHILDSTATUS, or
> | > | is that a wrong expectation.
> | >
> | > It's a wrong expectation. There's a zillion things that can go wrong,
> | > each with their own error code. Check the errorcode manpage for some
> | > possible values:
> | >
> | > https://www.tcl.tk/man/tcl/TclCmd/tclvars.htm#M12
>>
> | OK, thanks. I will implement that.
>
> One notable case is when the external program exits successfully, but
> prints to stderr:
>
> % catch {exec sh -c {echo foo >&2}} err opts
> 1
>
> % set errorCode
> NONE

In a future version I should take care of that also. But at the moment
that is not a problem for me.

Cecil Westerhof

unread,
Aug 17, 2018, 7:28:05 PM8/17/18
to
Not in this case, because I will not get the output of the command.

Cecil Westerhof

unread,
Aug 17, 2018, 7:59:04 PM8/17/18
to
Ralf Fassel <ral...@gmx.de> writes:

> * Cecil Westerhof <Ce...@decebal.nl>
> | > So general processing would be:
> | > if catch ...
> | > switch -- [lindex $::errorCode 0] {
> | > CHILDSTATUS {
> | > # CHILDSTATUS pid code
> | > # This format is used when a child process has exited with a
> | > # non-zero exit status. [...]
> | > }
> | > ...
>>
> | I use:
> | if {[lindex ${::errorCode} 2] == 3} {
>>
> | After an exec I expect the first element to be always CHILDSTATUS, or
> | is that a wrong expectation.
>
> It's a wrong expectation. There's a zillion things that can go wrong,
> each with their own error code. Check the errorcode manpage for some
> possible values:
>
> https://www.tcl.tk/man/tcl/TclCmd/tclvars.htm#M12
>
> | A status 3 is not an error (only means the service is not running), so
> | in that case I accept the output with the error message removed if it
> | is there.
>
> That's ok only if the first parameter in errorCode is CHILDSTATUS.
> Any other error could by chance have a 3 in there, too with a completely
> different meaning (yes, I know, your approach will work 99% of the times).

Implemented:
proc getCleanOutput {command nonerrors} {
if {[catch {exec {*}${command}} status]} {
switch -- [lindex ${::errorCode} 0] {
CHILDSTATUS {
set errorStr "\nchild process exited abnormally"
set length [string length ${errorStr}]
set retCode [lindex ${::errorCode} 2]
if {[regexp "${errorStr}\$" ${status}]} {
set status [string range ${status} 0 end-${length}]
}
if {! (${retCode} in ${nonerrors})} {
error "'${command}' returned ${retCode}\n${status}\n"
}
}
default {
error [format "'%s' gave unexpected error:\n%s\n%s" \
${command} ${::errorCode} ${::errorInfo}]
}
}
}
return ${status}
}

Should also do something for the NONE value.

Schelte Bron

unread,
Aug 18, 2018, 7:14:22 AM8/18/18
to
Cecil Westerhof wrote:
> I will not get the output of the command.

Of course you can get the output, if that's what you want:

try {
set status [exec systemctl status $service]
} trap CHILDSTATUS {msg opts} {
# Command produced a non-zero exit code
puts "Exit code: [lindex [dict get $opts -errorcode] 2]"
set strip "\nchild process exited abnormally"
set status [string range $msg 0 [string last $strip $msg]-1]
} on error msg {
puts stderr $msg
exit 99
}
puts "\nStatus:\n$status"


Schelte

Cecil Westerhof

unread,
Aug 18, 2018, 5:59:05 PM8/18/18
to
No, you cannot. When using:
try {
set status [exec echo foo; return 3]
} trap CHILDSTATUS {msg opts} {
# Command produced a non-zero exit code
puts "Exit code: [lindex [dict get $opts -errorcode] 2]"
set strip "\nchild process exited abnormally"
set status [string range $msg 0 [string last $strip $msg]-1]
} on error msg {
puts stderr $msg
exit 99
}
puts "\nStatus:\n$status"

You get:
Status:
child process exited abnormally

So I do not get the output from the command.

Eric

unread,
Aug 19, 2018, 6:10:05 AM8/19/18
to
You have broken it by trying to use the return to force an error status.
However, as the Tcl manpage says under Command Substitution,

The result of the script (i.e. the result of its last command) is
substituted into the word in place of the brackets and all of the
characters between them.

so what you get in status is the output of return, not of the exec!

Eric
--
ms fnd in a lbry

Cecil Westerhof

unread,
Aug 19, 2018, 7:28:04 AM8/19/18
to
You are right, my check was not correct. I need to rewrite it again.
;-)

Cecil Westerhof

unread,
Aug 23, 2018, 2:59:04 AM8/23/18
to
When an exec does not receive a 0 the string:
child process exited abnormally

is sometimes added, but not always. It seems it is added when there is
no output on stderr and not when there is output on stderr. Is this
correct, or just coincidence?

Rich

unread,
Aug 23, 2018, 7:12:22 AM8/23/18
to
Cecil Westerhof <Ce...@decebal.nl> wrote:
> When an exec does not receive a 0 the string:
> child process exited abnormally
>
> is sometimes added, but not always. It seems it is added when there is
> no output on stderr and not when there is output on stderr. Is this
> correct, or just coincidence?

Use the source luke:

1) download tcl source
2) unpack tcl source
3) in top level directory, do:
find -type f -print0 | xargs -0 grep "child process exited abnormally"
4) Ten hits will be returned, eight are from tcl-test files, one is the
changelog, and a single source file (generic/tclPipe.c) is "the one"
5) View "the one" file, looking at the code surrounding the message,
which reads:

/*
* If a child exited abnormally but didn't output any error
* information at
* all, generate an error message here.
*/

if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
"child process exited abnormally", -1));
}

So, the definition for when the "child ..." message is output is
"abnormal exit" of child and "no stderr output" of child.

6) remove the Tcl sources (if you don't want to 'grep' them for other
tidbits later

SYEDA

unread,
Aug 11, 2021, 3:46:55 PM8/11/21
to
Hello i have downloaded active state tcl version 2.4 for windows 10 , for spin model checker. I have been stuck on one error from like 2 days . i dunno what to do . I have run the spin.tcl for automata view but when i am running the file its giving me this error

./pan -D > dot.tmp
child process exited abnormally
I dont even know whats the problem .? the system path is set all good .Kindly help me with this issue

Uwe Klein

unread,
Aug 12, 2021, 3:57:46 PM8/12/21
to
Am 11.08.21 um 21:46 schrieb SYEDA:
does your tcl file use [exec] ?

see:
uwe@ben:~>tclsh
%
% exec true
%
% exec false
child process exited abnormally
%

the unix utility 'false' has a non zero return ( while 'true' returns zero

i.e. ./pan ( whatever executable that is ) returns a non zero value.

Uwe



0 new messages