我在 C 中为我正在编写的 shell 实现一个管道,该管道应该能够支持像 ls | 这样的东西。grep | grep | grep (换句话说,它应该是递归的)。
Shell 使用 winapi 在 cygwin 上运行
我为此找到了两种方法:使用进程或线程。
这是我当前的管道功能:
static void parcmd (void *arg){
cmddata cdata = *(cmddata*) arg;
parse_command(cdata.c, cdata.level, cdata.father, cdata.h);
}
static bool do_on_pipe(command_t *cmd1, command_t *cmd2, int level, command_t *father)
{
bool bRes;
SECURITY_ATTRIBUTES sa;
HANDLE hRead, hWrite;
DWORD dwRes1, dwRes2;
handledata hPipeIn, hPipeOut;
cmddata cmd1_data, cmd2_data;
HANDLE thread1, thread2;
ZeroMemory(&sa, sizeof(sa));
sa.bInheritHandle = TRUE;
bRes = CreatePipe(&hRead, &hWrite, &sa, 0);
//printf("\n\ndo_on_pipe debug write = %d read = %d\n", hWrite, hRead);
hPipeIn.pipeIn = INVALID_HANDLE_VALUE;
hPipeIn.pipeOut = hWrite;
cmd1_data.c = cmd1;
cmd1_data.level = level + 1;
cmd1_data.father = father;
cmd1_data.h = &hPipeIn;
thread1 = (HANDLE) _beginthreadex(NULL, 0, parcmd, &cmd1_data, 0, NULL);
//
hPipeOut.pipeIn = hRead;
hPipeOut.pipeOut = INVALID_HANDLE_VALUE;
cmd2_data.c = cmd2;
cmd2_data.level = level + 1;
cmd2_data.father = father;
cmd2_data.h = &hPipeOut;
thread2 = (HANDLE) _beginthreadex(NULL, 0, parcmd, &cmd2_data, 0, NULL);
//
//DIE(dwRes1 == WAIT_FAILED, "WaitForSingleObject - thd1");
dwRes1 = WaitForSingleObject(thread1, INFINITE);
dwRes2 = WaitForSingleObject(thread2, INFINITE);
//DIE(dwRes2 == WAIT_FAILED, "WaitForSingleObject - thd2");
CloseHandle(hWrite);
CloseHandle(hRead);
return 0;
}
我的问题是,在这种当前形式下,第二个线程,即在 (cmd1 | cmd2) 中执行第二个命令的线程并没有停止,基本上,我没有得到我的提示。
如果我移动 dwRes1 = WaitForSingleObject(thread1, INFINITE); 在创建第二个线程之前,我能够成功地执行类似于 (cmd1 | cmd2) 的管道命令,但是,在 cmd1 | 命令2 | cmd3, cmd2 打印到标准输出,而 cmd3 永远不会停止。
这是简单的命令函数(它从解析器获取输入并执行它):
bool parse_simple(simple_command_t *s, int level, command_t *father, void *h)
{
/* TODO sanity checks */
STARTUPINFO si;
PROCESS_INFORMATION pi;
DWORD dwRes;
BOOL bRes;
HANDLE inHandle = INVALID_HANDLE_VALUE;
HANDLE outHandle = INVALID_HANDLE_VALUE;
HANDLE errHandle = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES sa;
handledata *hd;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&sa, sizeof(sa));
sa.bInheritHandle = TRUE;
if (s->in != NULL) {
inHandle = getInputHandle(get_word(s->in), sa);
}
if (s->out != NULL && s->err != NULL
&& lstrcmp(get_word(s->out), get_word(s->err)) == 0) {
outHandle = getOutErrHandle(get_word(s->out), sa, IO_REGULAR);
errHandle = outHandle;
} else {
if (s->out != NULL) {
if(s->io_flags == IO_OUT_APPEND) {
outHandle = getOutErrHandle(get_word(s->out), sa, IO_OUT_APPEND);
} else {
outHandle = getOutErrHandle(get_word(s->out), sa, IO_REGULAR);
}
}
if(s->err != NULL) {
if(s->io_flags == IO_ERR_APPEND) {
errHandle = getOutErrHandle(get_word(s->err), sa, IO_ERR_APPEND);
} else {
errHandle = getOutErrHandle(get_word(s->err), sa, IO_REGULAR);
}
}
}
if(h != NULL) {
//printf("we got unnull h\n");
fflush(stdout);
hd = (handledata *) h;
if (hd->pipeIn != INVALID_HANDLE_VALUE) {
inHandle = hd->pipeIn;
}
if(hd->pipeOut != INVALID_HANDLE_VALUE) {
outHandle = hd->pipeOut;
}
//printf("\n\nhandle debug cmd = %s write = %d read = %d\n",get_argv(s), hd->pipeOut, hd->pipeIn);
fflush(stdout);
}
RedirectAllHandles(&si, inHandle, outHandle, errHandle);
//printf("\n\nhandles after redirect for cmd = %s write = %d read = %d err = %d\n",get_argv(s), outHandle, inHandle, errHandle);
fflush(stdout);
if (lstrcmp(get_word(s->verb), "exit") == 0 ||
lstrcmp(get_word(s->verb), "quit") == 0 ||
lstrcmp(get_word(s->verb), "cd") == 0)
{
//internal command
if (lstrcmp(get_word(s->verb), "exit") == 0 ||
lstrcmp(get_word(s->verb), "quit") == 0) {
shell_exit();
} else if (lstrcmp(get_word(s->verb), "cd") == 0) {
return shell_cd(s->params);
}
} else
if (strchr(get_word(s->verb), '=') != NULL) {
// env val
} else {
bRes = CreateProcess(
NULL, /* No module name (use command line) */
get_argv(s), /* Command line */
NULL, /* Process handle not inheritable */
NULL, /* Thread handle not inheritable */
TRUE, /* Set handle inheritance to FALSE */
0, /* No creation flags */
NULL, /* Use parent's environment block */
NULL, /* Use parent's starting directory */
&si, /* Pointer to STARTUPINFO structure */
&pi /* Pointer to PROCESS_INFORMATION structure */
);
if (!bRes)
{
printf("Execution failed for '%s'\n", get_argv(s));
return false;
}
//printf("cmd %s waiting for process\n", get_argv(s));
dwRes = WaitForSingleObject(pi.hProcess, INFINITE);
//todo add die
//printf("cmd %s process done\n", get_argv(s));
bRes = GetExitCodeProcess(pi.hProcess, &dwRes);
//todo add die
//printf("cmd %s got to handles\n", get_argv(s));
CloseHandle(inHandle);
CloseHandle(outHandle);
CloseHandle(errHandle);
//printf("cmd %s got to closed handles\n", get_argv(s));
return dwRes;
}
return 0; /* TODO replace with actual exit status */
}
以及根据情况决定调用什么函数的函数:
int parse_command(command_t *c, int level, command_t *father, void *h)
{
/* TODO sanity checks */
int status;
if (c->op == OP_NONE) {
/* TODO execute a simple command */
return parse_simple(c->scmd, level, c, h);
return 0; /* TODO replace with actual exit code of command */
}
switch (c->op) {
case OP_SEQUENTIAL:
/* TODO execute the commands one after the other */
parse_command(c->cmd1, level + 1, c, h);
return parse_command(c->cmd2, level + 1, c, h);
break;
case OP_PARALLEL:
/* TODO execute the commands simultaneously */
return do_in_parallel(c->cmd1, c->cmd2, level + 1, c);
break;
case OP_CONDITIONAL_NZERO:
/* TODO execute the second command only if the first one
* returns non zero */
status = parse_command(c->cmd1, level + 1, c, h);
if (!status)
return status;
return parse_command(c->cmd2, level + 1, c, h);
break;
case OP_CONDITIONAL_ZERO:
status = parse_command(c->cmd1, level + 1, c, h);
if (status)
return status;
return parse_command(c->cmd2, level + 1, c, h);
/* TODO execute the second command only if the first one
* returns zero */
break;
case OP_PIPE:
do_on_pipe(c->cmd1, c->cmd2,level, c);
/* TODO redirect the output of the first command to the
* input of the second */
break;
default:
return SHELL_EXIT;
}
return 0; /* TODO replace with actual exit code of command */
}
我知道这是一个庞大的代码库,不太可能得到任何响应,但我已经连续两到三天解决这个问题,我根本看不到解决方案,我怎样才能实现像 (cmd1 | cmd2 ... | cmdN) 使用此代码?谢谢!