execl(“/ bin / bash”,“bash”,“ – l”,“ – c”,“env”,NULL),怎么了?

我想使用execl("/bin/bash","bash","-l","-c","env",NULL)来获取环境variables,我使用参数"-l"的原因是我不需要源码"/etc/profile""~/.bash_login"等等。 但是当我运行它,程序被暂停,我必须使用Ctrl + C或Ctrl + D来停止它? 你能告诉我如何修改它吗?

代码如下所示,getPtrArray用于将一维数组更改为二维数组。

 int pid; int fd[2]; char buffer[10000]; char** envi; int res=pipe(fd); //create a child process to get environment variable if((pid=fork())==0){ close(fd[0]); dup2(fd[1],STDOUT_FILENO); struct passwd *pw=getpwnam("hgchen"); char *shell_type=pw->pw_shell; if(execl("/bin/bash","bash","-l","-c","env",(char*)0)<0){ printf("Error\n"); } exit(0); } // main process else{ wait(NULL); close(fd[1]); int nbytes=read(fd[0],buffer,sizeof(buffer)); envi=getPtrArray(buffer); } 

编辑注意:这是对原始示例代码的完整重写,因为OP发布了代码,我意识到它会导致bash阻塞标准输出,而不是像我原先想象的那样输入。 原因是bash输出被重定向到一个管道,没有任何管道读取,直到孩子退出。

execl()之前,请从/dev/nullSTDERR_FILENO重新打开/dev/null打开STDIN_FILENO 。 当STDOUT_FILENO (标准输出)被重定向到一个管道时,你不能wait()子进程退出:你必须在子进程运行时主动从管道读取数据。

考虑这个例子程序。 它需要一个命令行参数,即用户名。 (没有任何参数,或只是-h--help它输出短的使用信息。)

它获得与该用户名相对应的struct passwd ,创建存储在该结构体中的用户shell的路径的副本。 它派生一个子进程,在子进程中执行path-to-shell shell-name -c env ,将输出捕获到动态分配的数组(使用execute()函数)。 然后,为了简单起见,主要将输出写入原始标准输出。 您可以省略最后的while () { ... }循环来查看输出是否真正捕获到动态分配的数组。

请注意,我并没有真正验证所有 shell都支持-c语法。 我知道bashsh (原Bourne shell), dash (POSIX shell), tcshzsh都可以 – 覆盖我的/etc/shells所有shell,也就是允许的shell文件 – 所以它应该在实践中工作; 我不能保证。

 #define _POSIX_C_SOURCE 200809L #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> #include <fcntl.h> #include <pwd.h> #include <string.h> #include <errno.h> /* Open file or device to the specified descriptor. * Will never create files. * Returns 0 if success, errno otherwise. */ static int reopen(const int descriptor, const char *const path, const int flags) { int result, fd; if (descriptor == -1) return errno = EBADF; if (!path || !*path || flags & O_CREAT) return errno = EINVAL; do { fd = open(path, flags); } while (fd == -1 && errno == EINTR); if (fd == -1) return errno; if (fd == descriptor) return errno = 0; do { result = dup2(fd, descriptor); } while (result == -1 && errno == EINTR); if (result == -1) return errno; do { result = close(fd); } while (result == -1 && errno == EINTR); if (result == -1) return errno; return errno = 0; } /* Helper function: Close descriptor keeping errno unchanged. * Returns 0 if success, errno.h error code otherwise. */ static int closefd(const int descriptor) { if (descriptor != -1) { const int saved_errno = errno; int result; do { result = close(descriptor); } while (result == -1 && errno == EINTR); if (result == -1) result = errno; else result = 0; errno = saved_errno; return result; } else return EBADF; } /* Execute a command in a child process, capturing the output. * Standard input and error are redirected to /dev/null. * Returns zero if success, errno error code otherwise. */ int execute(const char *const cmdpath, const char *const args[], char **const dataptr, size_t *const sizeptr, size_t *const lenptr, int *const statusptr) { pid_t child, p; int out[2], result, *childstatus; char *data; size_t size, used = 0; ssize_t bytes; if (!cmdpath || !*cmdpath || !args || !args[0] || !dataptr || !sizeptr || !lenptr) return errno = EINVAL; /* Create the standard output pipe. */ if (pipe(out)) return errno; /* Fork the child process. */ child = fork(); if (child == (pid_t)-1) { closefd(out[0]); closefd(out[1]); return errno; } if (!child) { /* * Child process. */ closefd(STDIN_FILENO); closefd(STDOUT_FILENO); closefd(STDERR_FILENO); closefd(out[0]); /* Redirect standard output to the pipe. */ if (out[1] != STDOUT_FILENO) { do { result = dup2(out[1], STDOUT_FILENO); } while (result == -1 && errno == EINTR); if (result == -1) _exit(127); closefd(out[1]); } /* Open standard input from /dev/null. */ if (reopen(STDIN_FILENO, "/dev/null", O_RDONLY)) _exit(127); /* Open standard error to /dev/null. */ if (reopen(STDERR_FILENO, "/dev/null", O_WRONLY)) _exit(127); /* Execute the specified command. */ execv(cmdpath, (char **)args); /* Failed. */ _exit(127); } /* * Parent process. */ closefd(out[1]); if (*sizeptr > 0) { data = *dataptr; size = *sizeptr; } else { data = *dataptr = NULL; size = *sizeptr = 0; } while (1) { /* Grow data array if needed. */ if (used >= size) { size = (used | 32767) + 32769; data = realloc(data, size); if (!data) { kill(child, SIGTERM); do { p = waitpid(child, NULL, 0); } while (p == (pid_t)-1 && errno == EINTR); return errno = ENOMEM; } *dataptr = data; *sizeptr = size; } /* Read more data. */ do { bytes = read(out[0], data + used, size - used); } while (bytes == (ssize_t)-1 && errno == EINTR); if (bytes > (ssize_t)0) used += (size_t)bytes; else if (bytes == (ssize_t)0) break; /* All read (end of input) */ else { const int retval = (bytes == (ssize_t)-1) ? errno : EIO; kill(child, SIGTERM); do { p = waitpid(child, NULL, 0); } while (p == (pid_t)-1 && errno == EINTR); return errno = retval; } } /* We need to add the final '\0', which might not fit. */ if (used + 1 >= size) { size = used + 1; data = realloc(data, size); if (!data) { kill(child, SIGTERM); do { p = waitpid(child, NULL, 0); } while (p == (pid_t)-1 && errno == EINTR); return errno = ENOMEM; } *dataptr = data; *sizeptr = size; } data[used] = '\0'; if (lenptr) *lenptr = used; /* Reap the child process. */ if (statusptr) childstatus = statusptr; else childstatus = &result; do { p = waitpid(child, childstatus, 0); } while (p == (pid_t)-1 && errno == EINTR); if (p == (pid_t)-1) return errno; /* Success. */ return errno = 0; } /* A helper to write to standard error. Errno is kept unchanged. * Returns zero if success, errno error code otherwise. * Async-signal safe, in case you wish to use this safely in a signal handler. */ static int wrerr(const char *const message) { if (message && *message) { const int saved_errno = errno; const char *p = message; const char *q = message; ssize_t n; /* q = message + strlen(message), except that strlen() * is not an async-signal safe function. */ while (*q) q++; while (p < q) { n = write(STDERR_FILENO, p, (size_t)(q - p)); if (n > (ssize_t)0) p += n; else if (n != (ssize_t)-1) { errno = saved_errno; return EIO; } else if (errno != EINTR) { const int retval = errno; errno = saved_errno; return retval; } } errno = saved_errno; return 0; } else return 0; } const char *basename_of(const char *const string) { const char *r; if (!string) return NULL; r = strrchr(string, '/'); if (r && r[1]) return r + 1; return NULL; } int main(int argc, char *argv[]) { struct passwd *pw; char *shell; const char *args[4]; char *data = NULL; size_t size = 0; size_t used = 0; int status; if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { wrerr("\n"); wrerr("Usage: "); wrerr(argv[0]); wrerr(" [ -h | --help ]\n"); wrerr(" "); wrerr(argv[0]); wrerr(" USERNAME\n"); wrerr("\n"); return 1; } pw = getpwnam(argv[1]); if (!pw) { wrerr(argv[1]); wrerr(": "); wrerr(strerror(errno)); wrerr(".\n"); return 1; } if (pw->pw_shell && pw->pw_shell[0] == '/') shell = strdup(pw->pw_shell); else shell = strdup("/bin/sh"); args[0] = basename_of(shell); if (!args[0]) { wrerr(argv[1]); wrerr(": User has invalid shell, '"); wrerr(shell); wrerr("'.\n"); return 1; } args[1] = "-c"; args[2] = "env"; args[3] = NULL; if (execute(shell, args, &data, &size, &used, &status)) { wrerr("Failed to execute "); wrerr(shell); wrerr(": "); wrerr(strerror(errno)); wrerr(".\n"); return 1; } free(shell); /* Dump environment to standard output. */ { const char *p = data; const char *const q = data + used; ssize_t n; while (p < q) { n = write(STDOUT_FILENO, p, (size_t)(q - p)); if (n > (ssize_t)0) p += n; else if (n != (ssize_t)-1) { wrerr("Error writing to standard output.\n"); return 1; } else if (errno != EINTR) { wrerr("standard output: "); wrerr(strerror(errno)); wrerr(".\n"); return 1; } } } free(data); data = NULL; size = 0; used = 0; /* All done. */ return 0; } 

这是比真正必要(或首选)更低级别的代码; 你可以使用popen()和其他的stdio.h I / O函数来做同样的事情。

(我避开这些只是为了让自己更有趣。)

fprintf() / printf() / perror()不同的fprintf()wrerr()只是一个我喜欢使用的帮助函数,它是异步信号安全的并且忽略了信号传递( errno==EINTR )。 在这里,它是不需要的,你也可以使用例如fprintf() 。 (不像在网上可以看到的每一个例子, printf()等都不应该在信号处理程序中工作,它们通常是可以工作的,但是绝对没有保证, wrerr()可以工作,因为它是POSIX兼容。)

我还包括完整的错误检查。 有些错误的情况是不可能的,没有内核的错误,但我宁愿有他们无论如何。 你确实希望他们在你遇到错误的情况下,无论是在你自己的代码或其他地方。

在错误的情况下,我不打算释放动态分配的内存(虽然我可以),因为内核将自动处理。 但是,如果没有发生错误,程序会在从main()返回之前释放所有动态分配的内存。

有问题吗?

这并不直接回答你的问题,但使用popen(3)更容易。

这是测试和工作(在OSX下,而不是Linux):

 #include <stdio.h> int main(int argc, const char **argv) { char line[1024]; FILE *pipefp = popen("/bin/bash -l -c env", "r"); if (pipefp) { while (fgets(line, sizeof(line), pipefp)) { // Note: line contains newline printf("%s", line); } pclose(pipefp); } return 0; } 

找到导致命令暂停的原因的方法是添加“-x”选项。 而这个代码适合我:

 #include <stdio.h> #include <unistd.h> int main(int argc, char** argv) { int r = execl("/bin/bash", "bash", "-x", "-l", "-c", "env", NULL); printf("r: %d\n", r); /* This should not be printed or else execl has errors. */ return 0; } 

父母的wait是错误的。 您必须先阅读应用程序的输出, 然后等待。 正如在注释中已经解释的那样,shell正在等待您读取输出,并且您正在等待shell退出,因此您死锁了。

read 需要wait