0

I run a bash command from a c++ code using the system function provided in cstdlib. My question is not about the c++ implementation, but the one thing that is relevant to my question, is that I have to run multiple command on one line.

For instance, I run a gnuplot script contained in another directory. So my command is the following:

cd some/path; gnuplot -e gnuplot_file.gp; cd - > /dev/NULL

Both cd commands are important for personal reasons. I also want to "hide" the ouput in /dev/NULL.

Here is my question : how do I know the exit status of the gnuplot command ? Knowing if it failed is enough.

My problem is that I currently get the exit status of the last command, which is true even if the gnuplot failed.

I know that if I use && instead of ;, the last command won't be executed if the previous one fails and the exit status would then be false. But I need the last command to be executed...

What is the workaround ?

  • 2
    cd some/path; gnuplot ...; eval 'cd -- "$OLDPWD";' [ "'${?#1}'" ] – mikeserv Feb 5 '16 at 8:14
  • @mikeserv, thx a lot ! I wouldn't have found this by myself ! It looks ugly but it works ! – PinkFloyd Feb 5 '16 at 8:19
  • @mikeserv Wouldn't ! [ "${?#0}" ] make more sense? – Hauke Laging Feb 5 '16 at 8:26
  • @HaukeLaging - no, but it should be ${?##[!0]*} actually, though that's pushing it and so "[ $? -eq 0 ]" would be more sensible. Anyway, the point is to evaluate to null a failed $? so that [ returns true for a single, not-null arg or false for a single empty one. – mikeserv Feb 5 '16 at 8:36
  • @HaukeLaging - oh, yes! I'm sorry - i completely spaced the leading ! there. I do that a lot - im a spacey guy i guess. i always have to look at the most simple things like 5 times before i actually get it. just a dummy that way, i guess. but yours is better. – mikeserv Feb 5 '16 at 8:42
6

Drop the gnuplot into a subshell and then it's the last command executed. You also no longer require the last cd because the change of directory at the beginning of the subshell affects only the gnuplot, and so the redirection to /dev/null is also moot.

( cd some/path; gnuplot -e gnuplot_file.gp )

Perhaps you intended the redirection to /dev/null to apply to the entire command? (That's not what you've written in your question, though.)

( cd some/path; gnuplot -e gnuplot_file.gp ) >/dev/null

Finally, my preference for a snippet like this would be to run the gnuplot only if the initial cd succeeded. This would affect the exit status, in that you'd get a failed return if the change of directory failed, but is probably safer code

( cd some/path && gnuplot -e gnuplot_file.gp ) >/dev/null
  • Thanks for your answer ! I was not aware of the subshell option... This looks like a clean solution. You're saying that the final cd is useless because only the subshell is affected by the initial cd, how does this change if the initial cd fails ? – PinkFloyd Feb 8 '16 at 9:22
2

Save the return value in a variable:

cd some/path; gnuplot -e gnuplot_file.gp; gnuplot_ret=$?; cd "$OLDPWD"; exit $gnuplot_ret

Another way: Run in a subshell:

(cd some/path; gnuplot -e gnuplot_file.gp)

Since the directory change happened in a subshell, it doesn't affect your current shell and you don't need to cd back. And you get the exit status for free.

2

You could capture the exit code in a variable:

cd some/path; gnuplot -e gnuplot_file.gp ; a=$?

return to the previous directory (without any console output):

cd -- "$OLDPWD"

Print exit code

And then print the exit code captured (full line):

cd some/path; gnuplot -e gnuplot_file.gp ; a=$?; cd -- "$OLDPWD"; echo "$a"

If you need to re-create an exit code, there are many possible ways:

[ "x$a" == 'x0' ]
[ "$a" -eq 0 ]
[ ! "${a#0}" ]
[[ $a == 0 ]]
[[ $a -eq 0 ]]

It is recommendable not to use "${a#1}" as an error exit code could be any number other than 1. It is not guaranteed that a will be 1 for an error.

Or, as a is a number, if you accept to use an arithmetic solution (bash):

(( a == 0 ))
! (( a ))
(( ! a ))

Re-create exit code..

The full line would be:

cd some/path; gnuplot -e gnuplot_file.gp ; a=$?; cd -- "$OLDPWD"; (( ! a ))

The exit code will be 0 or 1, all errors will be reported with an exit code of 1.


Full exit code

An exit "$a" is useful only if it is useful to end the execution of the script.
Understand that that will preserve the actual exit code.

 cd some/path && gnuplot -e gnuplot_file.gp ; a=$?; cd -- "$OLDPWD"; exit "$a"

A 'return "$a"' may be useful if used inside a function.

function_gnuplot(){
    cd some/path && gnuplot -e gnuplot_file.gp ; a=$?
    cd -- "$OLDPWD"; return "$a"
}
  • Thank you for you well explained answer ! I sadly don't see why this answer is better than the one I got in the comments of my question. Your answer provides a nice way to return the full exit code but I don't need that much... – PinkFloyd Feb 8 '16 at 9:16
  • @PinkFloyd There are several differences: no use of (evil) eval, simple and clear IMO use of a variable to capture the exit code, explaining why ${a#1} is wrong, and the use of bash arithmetic ((! a )). That is beside the full exit code you mention. As to which is better? Well, it is up to you to make such judgement and select the one that works better for you. In my opinion, I like mine better, but I am not the one responsible of making such call. In either case, just Enjoy what you got. – user79743 Feb 8 '16 at 9:48
0

You could change the dir in your code before calling the system

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

#define BUFFSIZE 1024

int main(int argc, char **argv) {
  char old_dir[BUFFSIZE];
  getcwd(old_dir, BUFFSIZE); 

  chdir("some/path");

  int retvalue=system("gnuplot -e gnuplot_file.gp");

  chdir(old_dir);

  if (retvalue == -4) {
    /* do something ... */
  }
}

Note that this solution is not thread-safe because changes the current directory of the process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.