- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想使用 execl("/bin/bash","bash","-l","-c","env",NULL)
获取环境变量,the我使用参数 "-l"
的原因是我不需要 source "/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/null
重新打开STDIN_FILENO
,把STDERR_FILENO
打开到/开发/空
。当 STDOUT_FILENO
(标准输出)被重定向到管道时,您不能只是 wait()
让子进程退出:您必须在子进程运行时主动从管道读取.
考虑这个示例程序。它接受一个命令行参数,即用户名。 (没有任何参数或只是 -h
或 --help
它输出简短的使用信息。)
它获取与该用户名对应的struct passwd
,创建存储在该结构中的用户shell 路径的副本。它 fork 一个子进程,在子进程中执行 path-to-shell shell-name -c env
,捕获输出到一个动态分配的数组(使用 execute()
功能)。为简单起见,main 然后将输出写入原始标准输出。您可以省略最后的 while () { ... }
循环,以查看输出是否真正捕获到动态分配的数组。
请注意,我实际上并未验证所有 shell 都支持-c
语法。我确实知道 bash
、sh
(原始 Bourne shell)、dash
(POSIX shell)、tcsh
、和 zsh
都做 -- 覆盖我的 /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 函数执行相同的操作。
(我避开那些只是为了让我自己更感兴趣。)
wrerr()
只是我喜欢使用的辅助函数,不像 fprintf()
/printf()
/perror()
,它是异步信号安全的并且忽略信号传递(errno==EINTR
)。在这里,不需要,您可以使用例如fprintf()
也一样。 (与您在网上看到的几乎所有示例不同,printf()
等不应该在信号处理程序中工作。它们通常可以工作,但绝对不能保证。 wrerr()
将起作用,因为它符合 POSIX。)
我还包括完整的错误检查。如果没有内核错误,一些错误情况是不可能命中的,但无论如何我更喜欢拥有它们。在遇到错误时,您确实确实需要它们,无论是在您自己的代码中还是在其他地方。
在错误情况下,我不会费心去释放动态分配的内存(尽管我可以这样做),因为内核总是会自动处理。但是,如果没有发生错误,该程序会在从 main()
返回之前释放所有动态分配的内存。
有问题吗?
关于c - execl ("/bin/bash","bash","-l","-c","env",NULL),怎么了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18693100/
假设我 putenv 一个环境变量 ABC,然后执行 execl,或者我执行 execle 并将 ABC 添加到 envp 数组中我传递给 execle 的指针。 如果有的话,有区别吗? 最佳答案 p
我试图在 C++ 中运行 execl(),但它失败了,我不确定原因。 string fileName = "test.txt"; string computerName = "jssoile@
POSIX 系统公开了一系列 exec 函数,这些函数允许将可能不同的东西加载到当前进程中,保持打开的文件描述符、进程标识符等。 这可以出于多种原因完成,在我的例子中,这是自举——我想更改我自己进程的
我正在使用 c 中的 find 命令进行编程,但当我想对文件使用 exec 时,我被阻止了。 如果它是一个目录,那么很简单,我只使用 chdir(path) 但在文件上我有错误不是目录,所以我不能在上
if (fork_return==0) { printf("FROM THE CHILD PROCESS \n"); //print process id printf("The chil
我真的很喜欢这方面的一些调试帮助。我从早上到凌晨 4 点就一直在做这件事。 (我预计会在 7 小时内交付此内容 [上午 11 点]) main.c 中的所有内容都有效,但是当我创建一些子进程来使用 e
我试图理解这个系统调用(execle()),但我不知道它是如何工作的。我不知道如何使用 char* envp[],我们必须将其作为参数传递。我已经尝试过,但它不起作用: #include #incl
我有一个程序可以读取文件,对其进行处理并将结果放入输出文件中。当我有一个参数(输入文件)时,我创建输出文件并写入内容。 我创建了一个 fork() 以便将 stdout 重定向为 write() 内容
#include #include #include #define MAXLINE 512 main(int argc,char* argv[]){ int k; for (k
程序从配置文件中读取一些值,有些已定义,有些未定义,有些值为 0,有些处于事件状态。 我有以下代码: char *arg1="", *arg1_value="", *arg2="", *arg
你能帮我解决这个问题吗?execl 调用的程序“exam”和“students”执行了多少次?我认为正确的答案是程序“考试”有 8 个运行时间,“学生”有 0 个运行时间,因为在前两个 fork 中将
我正在制作一个 C 程序,该程序使用 execl 来执行 ls 命令来列出文件。如果在没有任何命令行参数的情况下调用程序,则会列出当前目录,如果用户将文件目录指定为命令行参数,则会列出该目录。 exe
我使用 TCC 编译并运行了以下程序在 Windows 7 下出现应用程序崩溃: #include int main(void) { if (execlp("c:\\windows\\sys
我正在使用 cywin 在 Windows 上编译 crashme 源代码,我遇到了一些编译错误。 错误: crashme.c: In function 'vfork_main': crashme.c
在UNIX环境下的系统编程上下文中,在使用C++编程语言时,在我的理解中,execl()会传入它要运行的程序的路径,以及一个vector。当传入该 vector 时,我将其理解为传入入口点,通常是 m
我有以下两个简单的程序: bye.cc #include int main() { std::cout #include #include #include using namespace
这个 execl 语句可能有什么问题?当我尝试运行它时,接收可执行文件提示 argc 小于 3。当我打印 argv 内容时,我得到以下信息: argv[1] = -1076146944 argv[2]
我的目标是学习如何执行 execle() 函数。我在 Head First C 书中找到了这段代码。 这是主进程代码(diner_info.c): #include #include int ma
在一个项目上工作,当我调用 execl() 时它不工作。它在 fork 之后调用,应该重新执行当前文件。(参数在文件的前面声明): argument = argv[0]; int err =execl
我正在尝试用 C 语言重现此命令: ls | wc > output.txt 因此,为此,我编写了以下程序: #include #include #include #include #incl
我是一名优秀的程序员,十分优秀!