C, sh, perl, system(): Can't transfer a return code appropriately: help, pls

I am using a perl-script from C-code, executing it by the 'system(..)' comand.

The problem is to return the perl-return code to the C correctly.

Default the 'system()' shell is Bourne: sh

My try: (perl_src.c_pl - the perl script; t_sys - C-program with system() call (I will show it after this block.))

> perl_src.c_pl ${fl} || { echo "$?, wrong;"; }
Can't use an undefined value as a symbol reference at ./perl_src.c_pl line 254, <$sdx> line 1013.
2, wrong;
>
>   # now execute the same command by the C-with system();
> t_sys "perl_src.c_pl ${fl} || { echo \"$?, wrong;\"; } "
Can't use an undefined value as a symbol reference at perl_src.c_pl line 254, <$sdx> line 1013.
0, wrong;

 executed command: perl_src.c_pl ../data/sdx4-1K.dat || { echo "0, wrong;"; }
 RETURN: 0
>

(the t_sys.c is:

> cat t_sys.c
#include <stdio.h>
main ( int argc, char* argv[] )
{
  char *cmd=argv[1];
  int rt=system(cmd);
  printf ("\n executed command: %s\n RETURN: %d\n", cmd, rt);
}
>

)

The problem is in the '$?' that is not correct in system() after the perl script is processed.

What I am doing wrong?
How I can transfer the correct perl return code to the C code?

Thnks!

To have a perl script return a certain error code to the system, do

exit 5;

in the perl script or the like.

But your perl script has bigger problems already, like the error message says. It may be returning unexpected errors due to the syntax problems in your script.

The system() function will exec a sh and return its exit status. The exit status of "perl_src.c_pl ${fl} || { echo \"$?, wrong;\"; }" will always be 0, unless the echo fails (which is very unlikely).

$ sh -c 'perl -e "exit 5" || echo perl exit status $?'
perl exit status 5
$ echo sh exit status $?
sh exit status 0

Something along the lines of the following is probably want you want:

sh -c 'perl ....; pexit=$?; [ $pexit -ne 0 ] && echo $pexit; exit $pexit'

Although, personally, I don't see the point of echoing from the shell pipeline (unless that was part of your troubleshooting process). You can check the exit status in the C code and generate the appropriate message there.

Regards,
Alister

I think, I am not clear enough.
Corona688 - I do not have a problem with the perl-script. The error is found and corrected.
I need to be sure that any perl-script problem (script could be changed) will be propagaited into C program and correctly ends the program!

alister - you are right, I am 'echo'ing the result to see that value now, when I debuging the situation.
The main intention is to 'exit $?' - expecting to have in C the same value as it was returned by the perl-script.
By now I exiting with hardwired '2' - as it returned currently by the perl (but it could be different!)

Anyway, by your ansvers, guys, I have realised one point: in the line

t_sys "perl_src.c_pl ${fl} || { echo \"$?, wrong;\"; } "

the '$?' is substituted by shell BEFORE starting the 't_sys' - because the 'double quotes'! :slight_smile:
My mistake here!

But, alister -

  • your point is correct and understood, but the problem is not in the overwriting the return code by echo, but in comming out after the system() - now I see that I had it passed in wrong way.
    Anyway, in correct passing way it still comming through the system() changed:
> sh -c 'perl -e "exit 5;" || exit $? ' ; echo $?
5
> t_sys 'perl -e "exit 5;" || exit $? ' ; echo $?

 executed command: perl -e "exit 5;" || exit $?
 RETURN: 1280
2
>

??? Now it even more unclear: Still system() returns something weird: 1280 instead of 5; but the program some why exiting with 2!!!
What is wrong??

Thank you for your attention!

First, you need to read the man page on system(3). The return value is of the format returned by wait(2). Thus, you need to feed that into the WEXITSTATUS macro to get the actual return value.

Second, you are not returning a value from your main function. The 2 is just a value left over on the stack.

Here's a quick cut at correcting your program (I'm not at a Linux system at the moment so I can't check it):

#include <stdio.h>
main ( int argc, char* argv[] )
{
  char *cmd=argv[1];
  int rt=system(cmd);
  int actual_exit = -1, actual_signal = -1;
  if (WIFEXITED(rt)) actual_exit = WEXITSTATUS(rt);
  else actual_signal = WTERMSIG(rt);
  printf ("\n executed command: %s\n RETURN: %d or SIGNAL: %d\n", 
    cmd, actual_exit, actual_signal);
  return actual_exit;
}

I took care of handling the case where the child process gets a signal though the program exit status won't reflect it correctly.

See if that helps.

1 Like

There are a few things wrong.

You aren't declaring main() to return an int, although the compiler is probably taking care of this for you.

main() is not returning a value.

rt is not the simple integer exit status that you think it is. rt contains other information regarding how the process exited (for example, if by a signal, which sig). You need to read the waitpid() man page for the details (which include macros to extract the actual exit status from system's return value).

system
wait

You omitted the stdlib.h include for system() and the wait-related macros required to work with its return value.

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
    char *cmd=argv[1];
    int rt=system(cmd);
    if (WIFEXITED(rt)) {
        rt=WEXITSTATUS(rt);
        printf ("\n executed command: %s\n RETURN: %d\n", cmd, rt);
    }
    else
        rt=128;
    return rt;
}

Sample run:

$ ./a.out 'perl -e "exit 5"'; echo $?

 executed command: perl -e "exit 5"
 RETURN: 5
5

Regards,
Alister

1 Like

Wow!
Thank you, guis, it is new for me, never come across of that WIFEXITED.
Sure, I should check the man for system().

Big thanks for expalanation!!

Just one more addition to this issue:

  • In my system the header for the WIFEXITED and WEXITSTATUS is:
    #include <sys/wait.h>
    not the 'stdlib.h'