-1

I am trying to return the value of a shell script. From what I have read it looks like i can use popen, but for some reason I can not get the code to work. Right now when I run the code I get garbage characters and not the float I am wanting.

The value form the shell script is a float.

I want to return the value of a bash script so I can use the returned value from the shell scrip as variables for my C code.

#include <stdio.h>

//Settings Variables
 float upload_speed;
 float download_speed;

int main(int argc, char **argv)
{

    FILE *upload_speed;

    //Runs shell script on code and returns data
    upload_speed = popen("./Filter_Data.sh Upload", "r");
    printf("return value is: %s", upload_speed);
    return 0;
}
Vlad
  • 145
  • 6
  • 19
  • 1
    Please post a [mcve], including what you expected to happen and what actually happened instead. – melpomene Sep 12 '18 at 18:41
  • @melpomene That is the code so far for the C part. – Vlad Sep 12 '18 at 18:46
  • 1
    Why are you including ``? Why do you have global variables (that are unused)? Have you ever done anything with files in C (e.g. using `fopen`, `fread`, `fgets`, etc)? – melpomene Sep 12 '18 at 18:48
  • 1
    What compiler options are you using? If you're using gcc, you should at minimum have `gcc -Wall -Wextra -pedantic -O2`. – melpomene Sep 12 '18 at 18:49
  • @melpomene, that was later on in the code, the value way going to be added to a table. I omitted that code for now. I am using geany, and my code complies fine. – Vlad Sep 12 '18 at 18:55
  • You cannot return a *floating point* value to the shell. At most, portably, you can return an `unsigned char` value between `0-255`. If you can ignore the fractional part of the floating point number (or simply provide an integer conversion for the value), you can return the result to the shell with the `return` statement. – David C. Rankin Sep 12 '18 at 18:56
  • Your code compiling fine is exactly the problem because it contains type errors that a compiler should warn about. – melpomene Sep 12 '18 at 18:57
  • @DavidC.Rankin would I add the return to the script? And then how would I get the value to the C program? – Vlad Sep 12 '18 at 18:58
  • No, no... The `return 0;` doesn't have to `return 0;`, it can `return val;` so if you can convert your string representation to a value between `0-255`, just `return that;` (note some OS's allow a wider range of return values, some allow 16-bit values. What OS are you on? – David C. Rankin Sep 12 '18 at 19:00
  • @DavidC.Rankin, I am sorry I am lost, where would the return call go? – Vlad Sep 12 '18 at 19:01
  • Right where you have `return 0;` currently. You would replace `return 0;` with `return your_value;`. That is what the `return` from `main()` does -- it returns the given value to your shell `:)` See [What is the valid range for program return value in Linux/bash? (and duplicates)](https://stackoverflow.com/questions/8082953/what-is-the-valid-range-for-program-return-value-in-linux-bash) – David C. Rankin Sep 12 '18 at 19:02
  • @DavidC.Rankin That will not work because, I have a few things things I want get from the shell script – Vlad Sep 12 '18 at 19:04
  • (Rankin scratching his head puzzled...) We are talking about the return from your C program correct? You are reading the upload speed from the shell script right? Then you convert that to an integer value in C (with `strtol`, `sscanf`, etc..), check the range of values, scale it so it returns Megabytes, Kilobytes (whatever makes the range `0-255`), then return that value at the end of your C program. If you are talking about another return, please clarify -- I'm not following you there. – David C. Rankin Sep 12 '18 at 19:07
  • @DavidC.Rankin my apologizes for the head scratching, I want to return the value of a bash script so I can use the returned value from the shell script as variables for my C code. – Vlad Sep 12 '18 at 19:09
  • What you're doing with `popen` is capturing the output the shell script produces. That's different from return values. – melpomene Sep 12 '18 at 19:10
  • @melpomene, oh I see, is there a way to get the return values from the shell script? – Vlad Sep 12 '18 at 19:12
  • @Vlad I'm not sure what you mean by return values in this context. Do you mean the exit status? If so, that's returned by `pclose`. – melpomene Sep 12 '18 at 19:16
  • Yes, if the shell script explicitly **returns** the upload speed then you can use the `system` call (see [system(3) - Linux manual page](http://man7.org/linux/man-pages/man3/system.3.html)), but the shell script must explicitly return the upload speed (e.g. `exit $upload_speed`) or the return will simply the the status of the last command executed (see the man page for how the return is handled). It may be easier just to have the script output the upload speed to `stdout` and then use the `popen/fgets` approach you started with. – David C. Rankin Sep 12 '18 at 19:18
  • when I try exit @upload_speed, i get this error on on the shell script exit: Upload:: numeric argument required – Vlad Sep 12 '18 at 19:24
  • `exit $upload_speed` is not equal to `exit @upload_speed` (you do have the variable named `upload_speed` initialized with the upload speed in your shell script -- right?) I figure your `'@'` is a typo right? – David C. Rankin Sep 12 '18 at 19:46
  • @DavidC.Rankin yes and yes, it was a typo and I do have a variable declared – Vlad Sep 12 '18 at 19:48
  • Run your shell script on the command line and then do `echo $?` and verify it is returning your value. – David C. Rankin Sep 12 '18 at 20:01
  • @DavidC.Rankin with exit or return at the end of the bash script? – Vlad Sep 12 '18 at 20:08
  • **After** the script has **exited** as the very next command before you do anything else. – David C. Rankin Sep 13 '18 at 00:45

1 Answers1

3

The popen function returns a pointer to a FILE object representing the input and output of the executed program. You're treating it as a string (i.e. a char *) which it is not.

You need to read from the file object to get its output:

FILE *outputFile;
char outputStr[80];

outputFile = popen("./Filter_Data.sh Upload", "r");
if (outputFile) {
  fgets(outputStr, sizeof(outputStr), outputFile);
  printf("output is: %s", outputStr);
  pclose(outputFile);
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • I see it returns the first line, is there a way to get it to return the last night result of the shell script? And thank you – Vlad Sep 12 '18 at 18:59
  • 1
    Need to use `pclose()`, not `fclose()`. – Shawn Sep 12 '18 at 19:00
  • @Vlad Knowing what the script outputs, you would need to read multiple lines and parse them to get the information you want. – dbush Sep 12 '18 at 19:00
  • @Shawn Good catch. Fixed. – dbush Sep 12 '18 at 19:01
  • @dbush and how would I parse them? Sorry, in all of the time i worked with C I never really interacted with fgets – Vlad Sep 12 '18 at 19:03
  • E.g. `fgets(outputStr, sizeof(outputStr), outputFile);` then I would just use a pointer or an array index to iterate over `outputStr` to find the first `[+-0-9]` and call `strtol` (or `strtof` if you actually want the float value), or use `sscanf` to accomplish the same (checking the return values and (and `ptr` & `endptr` in the case to `strtol`) to validate the conversion succeeded -- then you have a valid value you can scale and return. There are many examples on this site for `strtol` and `sscanf` conversions as well as how to parse or iterate over the string. – David C. Rankin Sep 12 '18 at 19:12
  • @dbush Than you, I can use your code to work for my project. One quick question, if I need to run the code more than once, since the shell output will change based on the argument I run through it, do I have to use the pclose after each iteration or just at the end once I am done with popen? – Vlad Sep 12 '18 at 19:31
  • @Vlad Each successful instance of `popen` must be paired with an instance of `pclose`. – dbush Sep 12 '18 at 19:32
  • @dbush Thank you =) – Vlad Sep 12 '18 at 19:34