tcl/Tk';无效的命令名"&引用';当通过管道进行控制时

tcl/Tk';无效的命令名"&引用';当通过管道进行控制时,c,tcl,tk,C,Tcl,Tk,我试图为一个C程序模拟一个简单的GUI,方法是将C程序分叉并在子程序中执行wish,然后从父程序向其中传输一系列tcl/tk命令。创建表单后,我会让C程序继续读取tcl程序的输出并响应它 我让它大部分工作正常,但在这个例子中,我一直从tcl得到一个信息: invalid command name "" 它没有破坏任何东西,但我真的不明白是什么导致它 #include <errno.h> #include <stdio.h> #include <stdlib.h&g

我试图为一个C程序模拟一个简单的GUI,方法是将C程序分叉并在子程序中执行
wish
,然后从父程序向其中传输一系列tcl/tk命令。创建表单后,我会让C程序继续读取tcl程序的输出并响应它

我让它大部分工作正常,但在这个例子中,我一直从tcl得到一个信息:

invalid command name ""
它没有破坏任何东西,但我真的不明白是什么导致它

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

#define DIE do { perror(__FUNCTION__); exit(EXIT_FAILURE); } while (0)
#define LEN(a) (sizeof(a) / sizeof(*a))

int child(int p2c[2], int c2p[2]) {
    if (close(p2c[1])) DIE;
    if (close(c2p[0])) DIE;

    if (dup2(p2c[0], fileno(stdin)) < 0) DIE;
    if (dup2(c2p[1], fileno(stdout)) < 0) DIE;

    char * cmds[] = { "wish", NULL };
    execvp(cmds[0], cmds);
    DIE;
}

int parent(int p2c[2], int c2p[2]) {
    if (close(p2c[0])) DIE;
    if (close(c2p[1])) DIE;

    char init[] = "button .b -text {Print} -command {puts hi}\n"
                  "button .x -text {Exit} -command {exit}\n"
                  "grid .b -column 0 -row 0\n"
                  "grid .x -column 0 -row 1\n";
    if (write(p2c[1], init, LEN(init)) < LEN(init)) DIE;
    if (close(p2c[1])) DIE;

    char buf[1<<10];
    ssize_t s;

    while ((s = read(c2p[0], buf, LEN(buf))) > 0) {
        buf[s-1] = '\0';

        printf("i read '%s'\n", buf);
    }

    return 0;
}

int main(int argc, char ** argv) {
    int p2c[2]; // parent to child pipe
    int c2p[2];

    if (pipe(p2c)) DIE;
    if (pipe(c2p)) DIE;

    switch (fork()) {
        case -1: DIE;              break;
        case  0: child(p2c, c2p);  break;
        default: parent(p2c, c2p); break;
    }

    return EXIT_SUCCESS;
}
#包括
#包括
#包括
#包括
#定义DIE do{perror(_函数_);exit(exit_FAILURE);}while(0)
#定义LEN(a)(sizeof(a)/sizeof(*a))
int-child(int-p2c[2],int-c2p[2]){
如果(关闭(p2c[1])模具;
if(close(c2p[0])死;
如果(dup2(p2c[0],fileno(stdin))<0)死亡;
如果(dup2(c2p[1],fileno(stdout))<0)死亡;
char*cmds[]={“wish”,NULL};
execvp(cmds[0],cmds);
死亡
}
int父级(int p2c[2],int c2p[2]){
如果(关闭(p2c[0])模具;
如果(关闭(c2p[1])模具;
char init[]=“button.b-text{Print}-命令{puts hi}\n”
“button.x-文本{Exit}-命令{Exit}\n”
“grid.b-第0列-第0行\n”
“grid.x-第0列-第1行\n”;
if(write(p2c[1],init,LEN(init))char buf[1问题在于,用于确定字符串长度的表达式是错误的。
strlen(init)
将生成一个小于
LEN(init)
返回值的值,因为后者在计数中包含终止NUL字符。这意味着您将该NUL写入管道Tcl将其解释为一个奇怪的(但合法的!)命令名。当然,没有这样的命令(除非您执行
proc\u0000{}{puts BOOM}
),但它仍然会检查脚本自动加载机制中知道的任何信息是否可以提供该命令。(将这个小过程添加到您发送过来的脚本中—记住将
\
加倍—会写出以下消息:

i read 'BOOM ' 我读到“轰 ' QED


如果您更改
写入
以实际发送正确的字节数,一切都会按照您的预期进行。

啊!是的,我故意用“\0”替换TCL返回的换行符,但我完全忘了在发送的内容中删除空字节。这是一个愚蠢的错误。谢谢!@scott\u fakename我们使用similaTcl实现中的r宏可以有效地处理字符串文本,但在那里我们还有一个额外的
-1