17

I am having trouble capturing output and exit codes inside a shell.

I need to compare exit codes from 2 scripts and if they don't match I want to echo the output of my 2 scripts.

What I currently have:

#!/bin/bash

resultA=$(./a.out 2>&1)
exitA=$?
resultB=$(./b.out 2>&1)
exitB=$?

Problem is a possible Segmentation Fault message is not captured, because it is directed to the error output of my current shell, but I need to capture everything including something like Segmentation Faults.

What is kind of a workaround and not as detailed as the real message:

#!/bin/bash

resultA=$(./a.out 2>&1)
exitA=$?
resultB=$(./b.out 2>&1)
exitB=$?
if [ $exitA == 139 ]; then
    resultA=$resultA"Segmentation Fault"
fi

This makes the words segmentation fault at least appear in my result variables.

9
  • 1
    I am a little confused: Your second example does not contain more subshells than the first one. Commented Mar 6, 2015 at 15:53
  • Is the problem here that you want to capture the segfault message in the variable and that isn't working? Commented Mar 6, 2015 at 15:57
  • Sorry @MichaelJaros I reedited my post. Commented Mar 6, 2015 at 16:03
  • @EtanReisner Yes I want to capture the segmentation fault message in the variables 'resultA' and 'resultB' as well. Commented Mar 6, 2015 at 16:04
  • You can't :) If you really need that text (e.g. for a report) best you could do is generate it in your script if your exit code indicates a SIGSEGV (see my answer below). Commented Mar 6, 2015 at 16:14

3 Answers 3

9

It's possible to capture the segfault error message, but you really need to work at it.

Here's one way:

outputA=$(bash -c '(./a)' 2>&1)

Here we create an child shell (with bash -c) whose stderr is redirected to stdout, and then get that child to execute the program in an explicit subshell. Errors inside the subshell will be captured by the child bash, which will then generate an error message (which is not quite the same as the message produced by an interactive bash):

$ echo $outputA
bash: line 1: 11636 Segmentation fault (core dumped) ( ./a )
Sign up to request clarification or add additional context in comments.

8 Comments

Wondering how this works, at least I can confirm that it works. I thought that the message will get issued by a program like apport (configured in /proc/sys/kernel/core_pattern). Seems that if bash runs in non-interactive mode it preserves the message
@hek2mgl: any process can trap dead children (through SIGCHLD), and bash does so. That's somewhat independent of the creation of a coredump; as you say, apport is triggered by the kernel when a coredump is produced (if you've left the configuration that way), but the coredump does not suppress the parent notification. (The parent is informed about the coredump. See sigaction, and search for CLD_DUMPED)
@hek2mgl: Also, apport doesn't produce the Segfault error message; bash does. The trick is getting a bash to redirect the error message, and making sure that the bash with the redirection is the one which produces the error message. Hence, two levels of indirection.
thanks for your explanation, especially for the info about CLD_DUMPED - I wondered how bash could know about the core dumped otherwise since it is not safe that a coredump will get created.
Thanks @rici another thing though, I said I also need the exit code, for comparisons between two program versions. Does that mean I would have to execute the program twice to get both the message and the exit code? Or can I somehow pass on the exit code of that subshell and save it in a variable? In my original post the first code block saves output & exit code (minus SEGFAULT MSG) in one execution.
|
7

Thanks to @rici this is the complete solution to my problem:

#!/bin/bash

resultA=$(bash -c '(./a.out); exit $?' 2>&1)
exitA=$?
resultB=$(bash -c '(./b.out); exit $?' 2>&1)
exitB=$?

2 Comments

why does running the programs have to be in parenthesis? Does this still work './a.out; exit $?' work?
@masonCherry: That's actually explained in my answer (albeit briefly). If a.out produces a segfault, then you need bash -c '(./a.out)' in order to capture the error message. You don't need exit $? to pass through the exit code. However, if you use ./a.out; exit $?, then you don't need the subshell (created with the parentheses) because bash -c needs to create a subshell in order to execute two commands.
0

Check out hek2mgl's answer for the reason for the missing message in your output.

The Bash manpage provides a hint towards a solution:

When a command terminates on a fatal signal N, bash uses the value of 128+N as the exit status.

You could use this to handle the special case of a signal killing one of your child processes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.