- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
file2.txt"时,它将创建文件,然后卡在以下一行:if(execvp(structVariables->a-6ren">
我正在创建一个可以读取命令的小外壳。当我运行程序并键入:"cat file.txt > file2.txt"
时,它将创建文件,然后卡在以下一行:if(execvp(structVariables->argv[0], argv) < 0).
(等待输入/输出?)。如果我使用ctrl + d结束程序,则可以在我的文件夹中看到该文件已创建,但未写入任何文件。 (dupPipe用于处理更多命令,由于上述问题尚未使用)
if((pid = fork()) < 0)
{
perror("fork error");
}
else if(pid > 0) // Parent
{
if(waitpid(pid,NULL,0) < 0)
{
perror("waitpid error");
}
}
else // Child
{
int flags = 0;
if(structVariables->outfile != NULL)
{
flags = 1; // Write
redirect(structVariables->outfile, flags, STDOUT_FILENO);
}
if(structVariables->infile != NULL)
{
flags = 2; // Read
redirect(structVariables->infile, flags, STDIN_FILENO);
}
if(execvp(structVariables->argv[0], argv) < 0)
{
perror("execvp error");
exit(EXIT_FAILURE);
}
}
int dupPipe(int pip[2], int end, int destinfd)
{
if(end == READ_END)
{
dup2(pip[0], destinfd);
close(pip[0]);
}
else if(end == WRITE_END)
{
dup2(pip[1], destinfd);
close(pip[1]);
}
return destinfd;
}
int redirect(char *filename, int flags, int destinfd)
{
int newfd;
if(flags == 1)
{
if(access(filename, F_OK) != -1) // If file already exists
{
errno = EEXIST;
printf("Error: %s\n", strerror(errno));
return -1;
}
newfd = open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if(newfd == -1)
{
perror("Open for write failed");
return -1;
}
}
else if(flags == 2)
{
newfd = open(filename, O_RDONLY);
if(newfd == -1)
{
perror("Open for read failed");
return -1;
}
}
else
return -1;
if(dup2(newfd, destinfd) == -1)
{
perror("dup2 failed");
close(newfd);
return -1;
}
if(newfd != destinfd)
{
close(newfd);
}
return destinfd;
}
最佳答案
看来您正在尝试编写一个Shell,以运行从输入读取的命令(如果不是这种情况,请编辑您的问题,因为尚不清楚)。
我不确定为什么您会在cat file.txt > file2.txt
这样的命令中使用管道,但无论如何都不会使用管道。让我们看看在bash之类的shell中键入cat file.txt > file2.txt
时会发生什么情况:
cat(1)
的位置创建子进程。 file2.txt
进行编写(稍后会对此进行更多介绍)。 open(2)
成功,则子进程将新打开的文件描述符复制到stdout
上(因此stdout
将有效地指向与file2.txt
相同的文件表条目)。 cat(1)
通过调用七个exec()
函数之一执行。参数file.txt
传递给cat(1)
,因此cat(1)
将打开file.txt
并读取所有内容,并将其内容复制到stdout
(重定向到file2.txt
)。 cat(1)
完成执行并终止,这将导致关闭和刷新所有打开的文件描述符。到cat(1)
终止时,file2.txt
是file.txt
的副本。 cat
),那么为什么还要使用管道呢?
redirect()
作为
STDOUT_FILENO
(而不是管道通道)来调用
destinfd
,以进行输出重定向。同样,输入重定向应使用
redirect()
调用
STDIN_FILENO
。这些常量是在
unistd.h
中定义的,因此请确保包括该标头。
exec()
失败,您可能还想退出子进程,否则您将运行2个Shell进程副本。
else if
。
if((pid = fork()) < 0)
{
perror("fork error");
}
else if(pid > 0) // Parent
{
if(waitpid(pid,NULL,0) < 0)
{
perror("waitpid error");
}
}
else // Child
{
int flags = 0;
if(structVariables->outfile != NULL)
{
flags = 1; // Write
// We need STDOUT_FILENO here
redirect(structVariables->outfile, flags, STDOUT_FILENO);
}
if(structVariables->infile != NULL)
{
flags = 2; // Read
// Similarly, we need STDIN_FILENO here
redirect(structVariables->infile, flags, STDIN_FILENO);
}
// This line changed; see updated answer below
if(execvp(structVariables->argv[0], structVariables->argv) < 0)
{
perror("execvp error");
// Terminate
exit(EXIT_FAILURE);
}
}
redirect()
函数易于出现竞争状况,因为在文件存在检查和实际文件创建之间存在时间间隔,在此期间另一个进程可以创建文件(这称为TOCTTOU错误:检查时间)使用时间)。您应该使用
O_CREAT | O_EXCL
原子地测试是否存在并创建文件。
newfd
。如果由于某种原因
newfd
和
destinfd
碰巧相同怎么办?然后,您将错误地关闭文件,因为如果传入两个相等的文件描述符,则
dup2(2)
本质上是无操作的。即使您认为这种情况永远不会发生,还是最好的做法是在关闭原始副本之前先检查复制的fd是否与原始fd不同。
int redirect(char *filename, int flags, int destinfd)
{
int newfd;
if(flags == 1)
{
newfd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
if(newfd == -1)
{
perror("Open for write failed");
return -1;
}
}
else if(flags == 2)
{
newfd = open(filename, O_RDONLY);
if(newfd == -1)
{
perror("Open for read failed");
return -1;
}
}
else
return -1;
if(dup2(newfd, destinfd) == -1)
{
perror("dup2 failed");
close(newfd);
return -1;
}
if (newfd != destinfd)
close(newfd);
return destinfd;
}
0666
中的
open(2)
替换为
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH
(确保包括
sys/stat.h
和
fcntl.h
)。您可能想使用
#define
使其更整洁,但我仍然认为,如果这样做的话,它比将某些魔术数字硬编码(这是主观的)更好,更具描述性。
dupPipe()
上发表评论,因为在此问题中不需要/未使用它。 I / O重定向就是您所需要的。如果要将讨论扩展到管道,请随时编辑问题或创建另一个问题。
cat(1)
挂起的原因是因为:
if (execvp(structVariables->argv[0], argv) < 0)
execvp(2)
的第二个参数应该是
structVariables->argv
,
而不是
argv
,因为
argv
是Shell程序的参数数组,(通常)为空。将空参数列表传递给
cat(1)
会使它从
stdin
而不是从文件读取,因此这就是它似乎挂起的原因-它正在等待您提供输入。因此,继续将该行替换为:
if (execvp(structVariables->argv[0], structVariables->argv) < 0)
cat < file.txt > file2.txt
这样的东西现在可以工作了(我测试了它)。
|
时,就会发生管道重定向。让我们通过一个示例来了解当键入
ls | grep "file.txt" | sort
时,幕后情况。了解这些步骤很重要,这样您就可以建立有关系统工作方式的准确心智模型。没有这样的愿景,您将不会真正理解实现:
ls
命令,grep
命令和sort
命令)。 exec()
函数之一来运行ls
。现在,记住管道意味着程序的输出是下一个的输入,因此在exec()
编码之前,外壳程序必须创建一个管道。将要运行ls(1)
的子进程在dup2(2)
之前调用exec()
,以将管道的写通道复制到stdout
上。同样,父进程调用dup2(2)
将管道的读取通道复制到stdin
上。理解这一步非常重要:因为父级将管道的读取端复制到stdin
,然后我们接下来要做的任何事情(例如再次执行fork来执行更多命令)将始终从管道中读取输入。因此,在这一点上,我们已经将ls(1)
写入stdout
,它被重定向到由外壳程序的父进程读取的管道。 grep(1)
。再次,它派生一个新进程来执行grep(1)
。请记住,文件描述符是通过fork继承的,并且父shell的进程将stdin
绑定到连接到ls(1)
的管道的读取端,因此即将执行grep(1)
的新子进程将从该管道“自动”读取!但是,等等,还有更多! Shell知道管道中还有另一个进程(sort
命令),因此在执行grep之前(在派生之前),shell创建了另一个管道以将grep(1)
的输出连接到sort(1)
的输入。然后,重复相同的步骤:在子进程上,将管道的写通道复制到stdout
上。在父级中,管道的读取通道被复制到stdin
上。同样,重要的是要真正了解这里发生的事情:将要执行grep(1)
的进程已经从连接到ls(1)
的管道中读取了输入,现在它的输出已连接到将馈送sort(1)
的管道。因此,grep(1)
本质上是从管道读取并写入管道。 OTOH,父shell进程将最后一个管道的读取通道复制到stdin
,从读取ls(1)
的输出有效地“放弃了”(因为grep(1)
仍然可以处理它),而是更新了输入流以从grep(1)
读取结果。 sort(1)
是最后一个命令,因此它只是派生+ execs sort(1)
。结果被写入stdout
,因为我们从未在shell进程中更改过stdout
,但是由于我们在步骤3中的操作,因此从连接grep(1)
和sort(1)
的管道读取了输入。stdout
上,并调用七个
exec()
函数之一。在父级上,我们关闭管道的写入通道,并将管道的读取通道复制到
stdin
上。
pipe(2)
重定向方之前,我们需要存储对原始Shell标准输入的引用,因为我们将(可能)在整个过程中对其进行多次更改。如果我们不保存它,我们可能会丢失对原始
stdin
文件的引用,然后我们将无法再读取用户输入!在代码中,我通常使用
fcntl(2)
和
F_DUPFD_CLOEXEC
(请参阅
man 2 fcntl
)执行此操作,以确保在子进程中执行命令时关闭描述符(通常不建议在使用时保留打开的文件描述符)。
wait(2)
。如果您考虑一下,它就很有意义:管道固有地同步了管道中的每个命令;仅当最后一条命令从管道读取
EOF
时,才假定命令集结束了(也就是说,我们知道只有当所有数据都流过整个管道时我们才完成)。如果外壳程序不等待最后一个进程,而是在管道的中间(或开始)等待其他进程,它将很快返回到命令提示符,而其他命令仍在运行背景-不是明智之举,因为用户希望外壳程序在等待更多任务之前完成当前作业。
int saved_stdin = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
if (saved_stdin < 0) {
perror("Couldn't store stdin reference");
break;
}
pid_t pid;
int i;
/* As long as there are at least two commands to process... */
for (i = 0; i < n-1; i++) {
/* We create a pipe to connect this command to the next command */
int pipefds[2];
if (pipe(pipefds) < 0) {
perror("pipe(2) error");
break;
}
/* Prepare execution on child process and make the parent read the
* results from the pipe
*/
if ((pid = fork()) < 0) {
perror("fork(2) error");
break;
}
if (pid > 0) {
/* Parent needs to close the pipe's write channel to make sure
* we don't hang. Parent reads from the pipe's read channel.
*/
if (close(pipefds[1]) < 0) {
perror("close(2) error");
break;
}
if (dupPipe(pipefds, READ_END, STDIN_FILENO) < 0) {
perror("dupPipe() error");
break;
}
} else {
int flags = 0;
if (structVariables[i].outfile != NULL)
{
flags = 1; // Write
if (redirect(structVariables[i].outfile, flags, STDOUT_FILENO) < 0) {
perror("redirect() error");
exit(EXIT_FAILURE);
}
}
if (structVariables[i].infile != NULL)
{
flags = 2; // Read
if (redirect(structVariables[i].infile, flags, STDIN_FILENO) < 0) {
perror("redirect() error");
exit(EXIT_FAILURE);
}
}
/* Child writes to the pipe (that is read by the parent); the read
* channel doesn't have to be closed, but we close it for good practice
*/
if (close(pipefds[0]) < 0) {
perror("close(2) error");
break;
}
if (dupPipe(pipefds, WRITE_END, STDOUT_FILENO) < 0) {
perror("dupPipe() error");
break;
}
if (execvp(structVariables[i].argv[0], structVariables[i].argv) < 0) {
perror("execvp(3) error");
exit(EXIT_FAILURE);
}
}
}
if (i != n-1) {
/* Some error caused an early loop exit */
break;
}
/* We don't need a pipe for the last command */
if ((pid = fork()) < 0) {
perror("fork(2) error on last command");
}
if (pid > 0) {
/* Parent waits for the last command to execute */
if (waitpid(pid, NULL, 0) < 0) {
perror("waitpid(2) error");
}
} else {
int flags = 0;
/* Execute last command. This will read from the last pipe we set up */
if (structVariables[i].outfile != NULL)
{
flags = 1; // Write
if (redirect(structVariables[i].outfile, flags, STDOUT_FILENO) < 0) {
perror("redirect() error");
exit(EXIT_FAILURE);
}
}
if (structVariables[i].infile != NULL)
{
flags = 2; // Read
if (redirect(structVariables[i].infile, flags, STDIN_FILENO) < 0) {
perror("redirect() error");
exit(EXIT_FAILURE);
}
}
if (execvp(structVariables[i].argv[0], structVariables[i].argv) < 0) {
perror("execvp(3) error on last command");
exit(EXIT_FAILURE);
}
}
/* Finally, we need to restore the original stdin descriptor */
if (dup2(saved_stdin, STDIN_FILENO) < 0) {
perror("dup2(2) error when attempting to restore stdin");
exit(EXIT_FAILURE);
}
if (close(saved_stdin) < 0) {
perror("close(2) failed on saved_stdin");
}
dupPipe()
的最后几点评论:
dup2(2)
和close(2)
都可能返回错误。您应该检查一下并采取相应的措施(即通过返回-1将错误传递到调用堆栈中)。 end
是READ_END
还是WRITE_END
,如果不正确,则返回错误(无论如何都不返回destinfd
,这可能使调用者代码获得错误的成功感)int dupPipe(int pip[2], int end, int destinfd)
{
if (end != READ_END && end != WRITE_END)
return -1;
if(end == READ_END)
{
if (dup2(pip[0], destinfd) < 0)
return -1;
if (pip[0] != destinfd && close(pip[0]) < 0)
return -1;
}
else if(end == WRITE_END)
{
if (dup2(pip[1], destinfd) < 0)
return -1;
if (pip[1] != destinfd && close(pip[1]) < 0)
return -1;
}
return destinfd;
}
关于c - 程序卡住了,管道文件描述符何时不应该打开?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31519435/
来自descriptor文档: A descriptor can be called directly by its method name. For example, d.__get__(obj).
概要 本人python理论知识远达不到传授级别,写文章主要目的是自我总结,并不能照顾所有人,请见谅,文章结尾贴有相关链接可以作为补充 全文分为三个部分装饰器理论知识、装饰器应用、装饰器延申
我正在查看 python 的描述 rune 档 here ,让我思考的陈述是: 对于物体,机械在 object.__getattribute__()转换 b.x进入 type(b).__dict__[
if((fd = creat(file_name,O_RDWR|S_IRWXU|S_IRWXG|S_IRWXO)) < 0){ perror("Create failed!");
长话短说Python 2.7.5,当使用描述符作为装饰器时,有没有办法传入参数(给 __init__ 方法)?或者如何使用带参数的方法装饰器 ( as here ) 访问类实例的属性? -- 我认为这
我试着用谷歌搜索一些关于它的东西。为什么非数据描述符适用于旧式类? 文档说他们不应该: “Note that descriptors are only invoked for new style ob
我升级到使用嵌入式 maven 3 的 netbeans 7。我有一个项目,其中包含许多模块和包含其他模块的模块。我的其他不依赖于内部项目的子模块可以在相同的配置下正常工作。在这种情况下,spring
我正在关注http://scikit-image.org/docs/0.11.x/auto_examples/plot_daisy.html ,但是不太清楚 desc[0],desc[1] 和 des
我有一个要求,其中有一个全局 FILE指针/描述符。其中一个函数将从该指针/描述符中读取。与FILE指针/描述符相关联的内部指针前进。此函数返回后,我想从同一个 FILE 指针/描述符中读取数据,并与
我正在编写一些描述符来封装数据验证,并想为它们编写测试。 我想知道是否应该通过在我的测试中创建描述符实例然后显式调用 __get__ 或 __set__ 方法来测试它们。 或者我应该在我的测试文件中创
我有这个 python 描述符: # Date Descriptor class DateAttribute(): def __init__(self, value=None):
分割: @font-face { font-family: 'RobotoLight'; src: url('../font/jura-demibold.eot'); src: url('../fon
我正在编写一个配置卷的存储自动化模块。我没有传递在存储 Controller 上实际创建卷所需的六个或更多参数,而是使用 __slots__ 创建了一个参数类,它被传递到 create 方法中,如下所
在我的应用程序中,我必须使用静态摄像头跟踪大学讲座中的讲师。目前我正在使用 Emgu CV 的默认 GPUHOGDescriptor,如果讲师的整个 body 都可见,它会很好用。在讲师站在 tabl
大家好,我正在使用 opencv3 和 contrib。问题是我想计算给定像素的筛选描述符(不使用检测到的关键点)。 我正在尝试用给定的像素构建一个关键点向量。但是,要创建关键点,除了像素位置外,我还
我正在使用 OpenCV 中的 HOGDescriptor 类进行对象检测。在我看来,该实现仅使用无符号渐变,因此无法区分亮->暗和暗->亮过渡,这是我真正需要的功能。有谁知道使用有符号梯度的 HOG
我目前正在使用 OpenCV 的 ORB 特征提取器,我确实注意到 ORB 描述符的存储方式很奇怪(至少对我来说是这样)(它基本上是一个 BRIEF-32,带有与我的问题无关的修改) .正如你们中的一
我想知道,在 MATLAB 中是否有针对“汽车”之类的对象而非人类的 HOG 描述符的任何实现? 但万一,只有人类,你能指导我找到那个代码,并给我一些提示,以改进代码以用于“汽车或摩托车等物体” 最佳
我正在尝试更好地理解描述符。 我不明白为什么在 foo 方法中描述符 __get__ 方法未被调用。 据我了解描述符 __get__ 当我通过点运算符访问对象属性或使用 __getattribute_
我想要一个类似于这个(无效)的结构: const uint8_t uArray[] = { uint8_t(sizeof(uArray)), 1, 2, 3 }; 并且 uArray[0] 应该是 4
我是一名优秀的程序员,十分优秀!