cshellcommand-lineevaldash-shell

Dash and C: eval "$(<cmdfile)" and system("eval \"\$(<cmdfile)\"") giving different results


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.


Solution

  • $(< …) 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'