Dash 和 C:eval "$(<cmdfile)" 和 system("eval \"\$(<cmdfile)\"") 给出不同的结果
Dash and C: eval "$(<cmdfile)" and system("eval \"\$(<cmdfile)\"") giving different results
我想使用 C 中的 system() 函数来计算文件中的表达式 cmdfile 但我不会得到与我直接在命令行上这样做时相同的结果。 cmdfile内容如下:
$ cat cmdfile
echo hello
当我直接在命令行上评估它的内容时,它起作用了:
$ eval "$(<cmdfile)"
hello
要在 C 中执行相同的操作,我使用的是 system()。这是我的代码:
$ 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;
}
问题是我在使用上面的代码时没有看到任何输出:
$ ./systest "eval \"$(<cmdfile)\""
eval "$(<cmdfile)"
应该在 printf 输出后立即打印 hello,但它不起作用。不过,我知道 system() 肯定在做某事,因为如果我给它一个不存在的文件名,dash 会抱怨:
$ ./systest "eval \"$(<cmdfileFF)\""
eval "$(<cmdfileFF)"
sh: 1: cannot open cmdfileFF: No such file
如果我只是评估 echo hello 而没有涉及 cmdfile,它也可以工作:
$ ./systest "eval \"echo hello\""
eval "echo hello"
hello
我想知道是什么导致了这种行为差异。还有其他方法可以在破折号中执行 cmdfile 的内容吗?我仅限于在命令行上使用 dash 的内置命令,因此无法使用 ./systest "eval \"$(cat cmdfile)\""
等选项。此外,"$(<cmdfile)"
的扩展应该只发生在 system() 内,而不是之前(因此 ./systest "eval \"$(<cmdfile)\""
将不起作用。
我用 dash 0.5.10.2-6 和 dash 0.5.8-2.1ubuntu2 测试了这个。
感谢您的任何见解!
编辑
感谢 Jonathan Leffler 的评论,我现在意识到破折号不理解 $(<file)
语法。那么什么是兼容 dash 的等价物?
总结
所以我的困惑是由于 system(...) 总是使用 /bin/sh,但是在命令行上测试我的表达式时,我不小心调用了 bash 而不是破折号.因此结果不同。
$(< …)
替换与 POSIX-sh 不兼容,但您的 sh 仅限于此。一般的替代方法是将 < cmdfile
替换为 cat cmdfile
:
./systest "eval \"$(cat cmdfile)\""
但我认为 dot-sourcing 在这种情况下是等效的:
./systest '. ./cmdfile'
正确的解决方法是在脚本中添加一个 shebang 行并将其标记为可执行文件。
#!/bin/sh
echo "hello"
shebang 必须绝对是文件的第一行(它的前两个字节应该是 #
和 !
)。 echo
的参数周围的引号在这里不是绝对必要的,而是一种很好的做法。 (另见 When to wrap quotes around a shell variable?)
更改权限只需在您刚刚创建文件时进行一次:
chmod +x ./cmdfile
现在,您只需使用
system("./cmdfile")
我想使用 C 中的 system() 函数来计算文件中的表达式 cmdfile 但我不会得到与我直接在命令行上这样做时相同的结果。 cmdfile内容如下:
$ cat cmdfile
echo hello
当我直接在命令行上评估它的内容时,它起作用了:
$ eval "$(<cmdfile)"
hello
要在 C 中执行相同的操作,我使用的是 system()。这是我的代码:
$ 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;
}
问题是我在使用上面的代码时没有看到任何输出:
$ ./systest "eval \"$(<cmdfile)\""
eval "$(<cmdfile)"
应该在 printf 输出后立即打印 hello,但它不起作用。不过,我知道 system() 肯定在做某事,因为如果我给它一个不存在的文件名,dash 会抱怨:
$ ./systest "eval \"$(<cmdfileFF)\""
eval "$(<cmdfileFF)"
sh: 1: cannot open cmdfileFF: No such file
如果我只是评估 echo hello 而没有涉及 cmdfile,它也可以工作:
$ ./systest "eval \"echo hello\""
eval "echo hello"
hello
我想知道是什么导致了这种行为差异。还有其他方法可以在破折号中执行 cmdfile 的内容吗?我仅限于在命令行上使用 dash 的内置命令,因此无法使用 ./systest "eval \"$(cat cmdfile)\""
等选项。此外,"$(<cmdfile)"
的扩展应该只发生在 system() 内,而不是之前(因此 ./systest "eval \"$(<cmdfile)\""
将不起作用。
我用 dash 0.5.10.2-6 和 dash 0.5.8-2.1ubuntu2 测试了这个。
感谢您的任何见解!
编辑
感谢 Jonathan Leffler 的评论,我现在意识到破折号不理解 $(<file)
语法。那么什么是兼容 dash 的等价物?
总结
所以我的困惑是由于 system(...) 总是使用 /bin/sh,但是在命令行上测试我的表达式时,我不小心调用了 bash 而不是破折号.因此结果不同。
$(< …)
替换与 POSIX-sh 不兼容,但您的 sh 仅限于此。一般的替代方法是将 < cmdfile
替换为 cat cmdfile
:
./systest "eval \"$(cat cmdfile)\""
但我认为 dot-sourcing 在这种情况下是等效的:
./systest '. ./cmdfile'
正确的解决方法是在脚本中添加一个 shebang 行并将其标记为可执行文件。
#!/bin/sh
echo "hello"
shebang 必须绝对是文件的第一行(它的前两个字节应该是 #
和 !
)。 echo
的参数周围的引号在这里不是绝对必要的,而是一种很好的做法。 (另见 When to wrap quotes around a shell variable?)
更改权限只需在您刚刚创建文件时进行一次:
chmod +x ./cmdfile
现在,您只需使用
system("./cmdfile")