gpt4 book ai didi

c - C-如何通过管道传递给仅从文件读取的程序

转载 作者:太空宇宙 更新时间:2023-11-04 02:27:46 24 4
gpt4 key购买 nike

我想用管道把一个字符串传递给一个只从文件读取输入的程序,而不是从stdin。使用bash,我可以做

echo "hi" | program /dev/stdin

我想从C代码中复制这种行为。我所做的就是
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>

int main() {
pid_t pid;
int rv;

int to_ext_program_pipe[2];
int to_my_program_pipe[2];

if(pipe(to_ext_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if(pipe(to_my_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}

if( (pid=fork()) == -1) {
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}

if(pid) {
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";

write(to_ext_program_pipe[1], string_to_write, strlen(string_to_write) + 1);
close(to_ext_program_pipe[1]);

wait(&rv);
if(rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}

char *string_to_read;
char ch[1];
size_t len = 0;
string_to_read = malloc(sizeof(char));
if(!string_to_read) {

fprintf(stderr, "%s\n", "Error while allocating memory");

exit(1);
}
while(read(to_my_program_pipe[0], ch, 1) == 1) {
string_to_read[len]=ch[0];
len++;
string_to_read = realloc(string_to_read, len*sizeof(char));
if(!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory");
}
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %s\n", string_to_read);
free(string_to_read);
} else {
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);

dup2(to_ext_program_pipe[0],0);
dup2(to_my_program_pipe[1],1);

if(execlp("ext_program", "ext_program", "/dev/stdin" , NULL) == -1) {
fprintf(stderr,"execlp Error!");
exit(1);
}
close(to_ext_program_pipe[0]);
close(to_my_program_pipe[1]);
}

return 0;
}

它不起作用。
编辑
我没有得到 ext_program输出,应该保存在 string_to_read中。程序挂起了。我可以看到 ext_program被执行了,但是我什么也没有得到
我想知道是否有错误,或者我想做的事情是否做不到。另外,我知道另一种选择是使用命名管道。
编辑2:更多详细信息
由于我仍然无法使我的程序正常工作,我发布了完整的代码
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

int main() {

pid_t pid;
int rv;
int to_phantomjs_pipe[2];
int to_my_program_pipe[2];

if(pipe(to_phantomjs_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}
if(pipe(to_my_program_pipe)) {
fprintf(stderr,"Pipe error!\n");
exit(1);
}

if( (pid=fork()) == -1) {
fprintf(stderr,"Fork error. Exiting.\n");
exit(1);
}

if(pid) {
close(to_my_program_pipe[1]);
close(to_phantomjs_pipe[0]);

char jsToExectue[] = "var page=require(\'webpage\').create();page.onInitialized=function(){page.evaluate(function(){delete window._phantom;delete window.callPhantom;});};page.onResourceRequested=function(requestData,request){if((/http:\\/\\/.+\?\\\\.css/gi).test(requestData[\'url\'])||requestData.headers[\'Content-Type\']==\'text/css\'){request.abort();}};page.settings.loadImage=false;page.settings.userAgent=\'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\';page.open(\'https://stackoverflow.com\',function(status){if(status!==\'success\'){phantom.exit(1);}else{console.log(page.content);phantom.exit();}});";

write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue) + 1);
close(to_phantomjs_pipe[1]);

int read_chars;
int BUFF=1024;
char *str;
char ch[BUFF];
size_t len = 0;
str = malloc(sizeof(char));
if(!str) {
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
str[0] = '\0';

while( (read_chars = read(to_my_program_pipe[0], ch, BUFF)) > 0)
{
len += read_chars;
str = realloc(str, (len + 1)*sizeof(char));
if(!str) {
fprintf(stderr, "%s\n", "Error while allocating memory");
}
strcat(str, ch);
str[len] = '\0';
memset(ch, '\0', BUFF*sizeof(ch[0]));
}
close(to_my_program_pipe[0]);
printf("%s\n", str);
free(str);

wait(&rv);
if(rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
} else {
dup2(to_phantomjs_pipe[0],0);
dup2(to_my_program_pipe[1],1);

close(to_phantomjs_pipe[1]);
close(to_my_program_pipe[0]);
close(to_phantomjs_pipe[0]);
close(to_my_program_pipe[1]);

execlp("phantomjs", "phantomjs", "--ssl-protocol=TLSv1", "/dev/stdin" , (char *)NULL);
}

return 0;
}

我要做的是将脚本通过管道传递给phantomjs,然后将得到的HTML作为字符串读取。我修改了代码,但phantomjs仍然没有从stdin中读取。
我通过创建一个哑程序来测试脚本字符串,该程序将脚本字符串写入一个文件,然后正常执行phantomjs,这是可行的。
我还尝试执行 execlp("phantomjs", "phantomjs", "--ssl-protocol=TLSv1", "path_to_script_file" , (char *)NULL);,这也起作用,输出HTML显示出来。
使用管道时不起作用。

最佳答案

最后的解释
PhantomJS的一些实验表明,问题在于在发送给PhantomJS的JavaScript程序的末尾写入一个空字节。
这突出了两个错误:
这个程序发送一个不必要的空字节。
PhantomJS 2.1.1(在运行macOS High Sierra 10.13.3的Mac上)在其他有效程序后跟空字节时挂起
问题中的代码包含:

write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue) + 1);

+ 1表示终止字符串的空字节也被写入 phantomjs。写入空字节会导致 phantomjs挂起。这相当于一个bug——当然不清楚为什么PhantomJS在没有检测到EOF(没有更多数据)和没有给出错误等情况下挂起。
将该行更改为:
write(to_phantomjs_pipe[1], jsToExectue, strlen(jsToExectue));

这段代码可以按预期工作——至少在运行macOS High Sierra 10.13.3的Mac上使用PhantomJS 2.1.1。
初步分析
您没有在子项中关闭足够的文件描述符。
经验法则:如果你
dup2()
标准输入或标准输出管道的一端,关闭
返回的原始文件描述符
pipe()
尽快。
尤其是,在使用任何
exec*()
功能系列。
如果将描述符与
dup()

fcntl()
F_DUPFD
显示的子代码是:
} else {
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);

dup2(to_ext_program_pipe[0],0);
dup2(to_my_program_pipe[1],1);

if(execlp("ext_program", "ext_program", "/dev/stdin" , NULL) == -1) {
fprintf(stderr,"execlp Error!");
exit(1);
}
close(to_ext_program_pipe[0]);
close(to_my_program_pipe[1]);
}

最后两个 close()语句永远不会执行;它们需要出现在 execlp()之前。
你需要的是:
} else {
dup2(to_ext_program_pipe[0], 0);
dup2(to_my_program_pipe[1], 1);
close(to_ext_program_pipe[0]);
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
close(to_my_program_pipe[1]);

execlp("ext_program", "ext_program", "/dev/stdin" , NULL);
fprintf(stderr, "execlp Error!\n");
exit(1);
}

您可以在拆分 close()调用时对其重新排序,但最好重新组合它们,如图所示。
注意,不需要测试 execlp()是否失败。如果它返回,它就失败了。如果成功了,它就不会回来了。
可能还有另一个问题。父进程等待孩子退出,然后从孩子那里读取任何内容。但是,如果子进程试图写入的数据超过管道的容量,则该进程将挂起,等待某个进程(必须是父进程)读取该管道。因为他们都在等待对方做某件事,然后才做对方在等待的事,所以这就是(或者,至少,可能是)僵局。
您还应该修改父进程,以便在等待之前进行读取。
if (pid) {
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";

write(to_ext_program_pipe[1], string_to_write, strlen(string_to_write) + 1);
close(to_ext_program_pipe[1]);

char *string_to_read;
char ch[1];
size_t len = 0;
string_to_read = malloc(sizeof(char));
if(!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
while (read(to_my_program_pipe[0], ch, 1) == 1) {
string_to_read[len] = ch[0];
len++;
string_to_read = realloc(string_to_read, len*sizeof(char));
if (!string_to_read) {
fprintf(stderr, "%s\n", "Error while allocating memory\n");
exit(1);
}
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %s\n", string_to_read);
free(string_to_read);

wait(&rv);
if (rv != 0) {
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
} …

我还重写了代码,以大块(1024字节或更多)读取。只是不要复制比读取返回更多的数据,仅此而已。反复使用 realloc()将一个字节分配给缓冲区最终会非常缓慢。如果只有几个字节的数据,这无关紧要;如果有千字节或更多的数据要处理,这也很重要。
稍后:由于PhantomJS程序响应发送的消息生成了超过90 KiB的数据,这是问题的一个因素-或者如果不是PhantomJS中的挂起空字节错误,这可能是问题的一个原因。
2018-02-03仍有问题
我将修改后的代码提取到一个程序中( pipe89.c,编译为 pipe89)。当分配的空间改变时,我遇到了不一致的崩溃。我最终意识到你重新分配一个字节的空间太小了——这比它应该做的要长得多(但如果Valgrind可以用于macOS High Sierra的话——现在还没有)。
下面是带有调试信息注释输出的固定代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
pid_t pid;
int rv;

int to_ext_program_pipe[2];
int to_my_program_pipe[2];

if (pipe(to_ext_program_pipe))
{
fprintf(stderr, "Pipe error!\n");
exit(1);
}
if (pipe(to_my_program_pipe))
{
fprintf(stderr, "Pipe error!\n");
exit(1);
}

if ((pid = fork()) == -1)
{
fprintf(stderr, "Fork error. Exiting.\n");
exit(1);
}

if (pid)
{
close(to_my_program_pipe[1]);
close(to_ext_program_pipe[0]);
char string_to_write[] = "this is the string to write";

write(to_ext_program_pipe[1], string_to_write, sizeof(string_to_write) - 1);
close(to_ext_program_pipe[1]);

char ch[1];
size_t len = 0;
char *string_to_read = malloc(sizeof(char));
if (string_to_read == 0)
{
fprintf(stderr, "%s\n", "Error while allocating memory");
exit(1);
}
string_to_read[len] = '\0';

while (read(to_my_program_pipe[0], ch, 1) == 1)
{
//fprintf(stderr, "%3zu: got %3d [%c]\n", len, ch[0], ch[0]); fflush(stderr);
string_to_read[len++] = ch[0];
char *new_space = realloc(string_to_read, len + 1); // KEY CHANGE is " + 1"
//if (new_space != string_to_read)
// fprintf(stderr, "Move: len %zu old %p vs new %p\n", len, (void *)string_to_read, (void *)new_space);
if (new_space == 0)
{
fprintf(stderr, "Error while allocating %zu bytes memory\n", len);
exit(1);
}
string_to_read = new_space;
string_to_read[len] = '\0';
}
close(to_my_program_pipe[0]);
printf("Output: %zu (%zu) [%s]\n", len, strlen(string_to_read), string_to_read);
free(string_to_read);

wait(&rv);
if (rv != 0)
{
fprintf(stderr, "%s %d\n", "phantomjs exit status ", rv);
exit(1);
}
}
else
{
dup2(to_ext_program_pipe[0], 0);
dup2(to_my_program_pipe[1], 1);
close(to_ext_program_pipe[0]);
close(to_ext_program_pipe[1]);
close(to_my_program_pipe[0]);
close(to_my_program_pipe[1]);

execlp("ext_program", "ext_program", "/dev/stdin", NULL);
fprintf(stderr, "execlp Error!\n");
exit(1);
}

return 0;
}

它在一个程序上进行了测试,该程序输出5590字节,输入27字节。这并不像你的程序那样是一个巨大的乘数,但它证明了一点。
我仍然认为最好不要一次重新分配一个额外的字节-扫描循环应该使用一个缓冲区,比如说,1 KiB,一次读取最多1 KiB,然后一次分配所有额外的空间。对于内存分配系统来说,这是一项强度要小得多的训练。
2018-02-05继续存在的问题
从Edit 2获取代码,只将函数定义从 int main() {更改为 int main(void) {(因为我使用的编译选项不允许使用旧式的非原型函数声明或定义,并且没有 void,即不是原型),代码是
为我工作很好。我创建了一个代理 phantomjs程序(从另一个已经存在的程序),如下所示:
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
for (int i = 0; i < argc; i++)
printf("argv[%d] = <<%s>>\n", i, argv[i]);
for (int i = 0; envp[i] != 0; i++)
printf("envp[%d] = <<%s>>\n", i, envp[i]);
FILE *fp = fopen(argv[argc - 1], "r");
if (fp != 0)
{
int c;
while ((c = getc(fp)) != EOF)
putchar(c);
fclose(fp);
}
else
fprintf(stderr, "%s: failed to open file %s for reading\n",
argv[0], argv[argc-1]);
return(0);
}

这段代码将回显参数列表和环境,然后打开名为最后一个参数的文件,并将其复制到标准输出。(由于对 argv[argc-1]的特殊处理,它是高度专业化的,但是之前的代码有时对于调试复杂的shell脚本非常有用。)
当我用这个“ phantomjs”运行您的程序时,我得到了预期的输出:
argv[0] = <<phantomjs>>
argv[1] = <<--ssl-protocol=TLSv1>>
argv[2] = <</dev/stdin>>
envp[0] = <<MANPATH=/Users/jleffler/man:/Users/jleffler/share/man:/Users/jleffler/oss/share/man:/Users/jleffler/oss/rcs/man:/usr/local/mysql/man:/opt/gcc/v7.3.0/share/man:/Users/jleffler/perl/v5.24.0/man:/usr/local/man:/usr/local/share/man:/usr/share/man:/opt/gnu/share/man>>
envp[1] = <<IXH=/opt/informix/12.10.FC6/etc/sqlhosts>>

envp[49] = <<HISTFILE=/Users/jleffler/.bash.jleffler>>
envp[50] = <<_=./pipe31>>
var page=require('webpage').create();page.onInitialized=function(){page.evaluate(function(){delete window._phantom;delete window.callPhantom;});};page.onResourceRequested=function(requestData,request){if((/http:\/\/.+?\\.css/gi).test(requestData['url'])||requestData.headers['Content-Type']=='text/css'){request.abort();}};page.settings.loadImage=false;page.settings.userAgent='Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36';page.open('https://stackoverflow.com',function(status){if(status!=='success'){phantom.exit(1);}else{console.log(page.content);phantom.exit();}});

在这一点上,我必须指出您的环境中的 phantomjs;当您执行下列操作时,它的行为似乎不像预期的那样:
echo "$JS_PROG" | phantomjs /dev/stdin | cat

当然,我不能再重复你的问题了。
你应该使用我的代理代码,而不是真正的代码,看看你得到了什么。
如果得到的输出与我展示的类似,那么问题就出在真正的 phantomjs上。
如果你没有得到与我展示的类似的输出,那么从更新到问题,你的代码可能有问题。
稍后:注意,因为 phantomjs使用 phantomjs来打印数据,所以它不会注意到发送给子级的无关空字节。

关于c - C-如何通过管道传递给仅从文件读取的程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48599507/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com