0

I would like to use the system() function in C to evaluate an expression within a file cmdfile but I'm not getting the same results as when I do so on the command line directly. The content of cmdfile is the following:

$ cat cmdfile
echo hello

and when I evaluate its content on the command line directly, it works:

$ eval "$(<cmdfile)"
hello

To do the same in C, I'm using system(). This is my code:

$ cat systest.c
#include <stdio.h>
#include <string.h>

int main (int argc, char* argv[])
{
    char* cmd = argv[1];
    printf("%s\n", cmd);
    system(cmd);
    return 0;
}

The trouble is that I don't see any output when using the above code:

$ ./systest "eval \"\$(<cmdfile)\""
eval "$(<cmdfile)"

There should be hello printed right after the printf output but it doesn't work. Still, I know that system() is definitely doing something, because if I give it a non-existing filename, dash complains:

$ ./systest "eval \"\$(<cmdfileFF)\""
eval "$(<cmdfileFF)"
sh: 1: cannot open cmdfileFF: No such file

and if I just evaluate echo hello without involving cmdfile, it works too:

$ ./systest "eval \"echo hello\""
eval "echo hello"
hello

I'd like to know what is causing this difference in behaviour. Is there any other way of executing the content of cmdfile in dash? I'm restricted to only using the built-in commands of dash on the command line, so options such as ./systest "eval \"\$(cat cmdfile)\"" are not possible. Further, the expansion of "$(<cmdfile)" should only happen within system(), not before (thus ./systest "eval \"$(<cmdfile)\"" won't work.

I tested this with dash 0.5.10.2-6 and dash 0.5.8-2.1ubuntu2.

Thank you for any insight!

Edit

Thanks to Jonathan Leffler's comment, I now realise that dash doesn't understand the $(<file) syntax. So what would be a dash-compatible equivalent?

Wrap-up

So my confusion was due to the fact that system(...) always uses /bin/sh, but when testing my expressions on the command line, I was accidentally invoking bash instead of dash. Hence the results were different.

Michel
  • 769
  • 4
  • 19
borizzzzz
  • 620
  • 1
  • 6
  • 17

2 Answers2

3

$(< …) substitution isn’t POSIX-sh-compatible, but your sh is restricted to about that. A general alternative is to replace < cmdfile with cat cmdfile:

./systest "eval \"\$(cat cmdfile)\""

but I think dot-sourcing is equivalent in this case:

./systest '. ./cmdfile'
Ry-
  • 218,210
  • 55
  • 464
  • 476
1

The proper fix is to put a shebang line in the script and mark it as executable.

#!/bin/sh
echo "hello"

The shebang needs to be the absolutely first line of the file (its first two bytes should be # and !). The quoting around the argument to echo is not strictly necessary here, but good practice. (See also When to wrap quotes around a shell variable?)

Changing the permissions only needs to be done once, when you have just created the file:

chmod +x ./cmdfile

Now, you can simply use

system("./cmdfile")
tripleee
  • 175,061
  • 34
  • 275
  • 318