Unix编程实践教程笔记(四) IO重定向
IO重定向
//将stdin(标准输入)重定向到文件的第一个方法
// 策略解释:
// close then open
// 先使用close将标准输入的连接切断
// 使用open打开连接到stdin的文件
// 当前可用的文件描述符最低位0,因此所打开的文件将被连接到标准输入上去
// 如此:所有从标准输入读取数据的函数都将从此文件中读入
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int fd;
char line[100];
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
close(0);
fd = open("/etc/passwd",O_RDONLY);
if(fd!=0)
{
fprintf(stderr,"open fd 0 failed\n");
exit(1);
}
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
return 0;
}
root@ziggy-virtual-machine:~/unix_linux/chapter10# ./stdinredir1
line1
line1
line2
line2
line3
line3
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
方法2 open close dup open
系统调用dup建立指向已经存在的文件描述符的第二个连接

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
//将stdin(标准输入)重定向到文件的第一个方法
// 策略解释:
// close then open
// 先使用close将标准输入的连接切断
// 使用open打开连接到stdin的文件
// 当前可用的文件描述符最低位0,因此所打开的文件将被连接到标准输入上去
// 如此:所有从标准输入读取数据的函数都将从此文件中读入
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<stdlib.h>
int main()
{
int fd;
int newfd;
char line[100];
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
fd = open("/etc/passwd",O_RDONLY);
close(0);
newfd = dup(fd);//对fd做一个复制,使用最低可用文件描述符,所以为0
if(newfd!=0)
{
fprintf(stderr,"could not duplicate fd to 0\n");
exit(1);
}
close(fd);
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
fgets(line,100,stdin);printf("%s",line);
return 0;
}
root@ziggy-virtual-machine:~/unix_linux/chapter10# ./stdinredir2
now
now
a
a
new input
new input
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
方法3 open dup2 close

为程序重定向IO,例如:who>userlist
fork后,子进程继承了父进程指向打开文件的指针
(1)初始情况:开始时,进程运行在用户空间,文件描述符1连接打开的文件f上
(2)fork后,在新的进程中,包含与父进程相同的代码,数据,和打开文件的文件描述符,因此1仍然是指向f
子进程调用close(1),父进程中的1仍然指向f,子进程中文件描述符变为最低未用文件描述符
子进程调用create("g",m);
1连接到g,子进程的标准输出重定向到g
调用who后:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<sys/types.h>
int main()
{
int pid;
int fd;
if((pid=fork())==-1)
{
perror("fork");
exit(1);
}
if(pid==0)
{
close(1);
fd =creat("userlist",0644);
execlp("who","who",NULL);
perror("execlp");
exit(1);
}
if(pid!=0)
{
wait(NULL);
printf("child run who done,results in userlist\n");
}
return 0;
}
/*
root@ziggy-virtual-machine:~/unix_linux/chapter10# ls
stdinredir1 stdinredir2 userlist whotofile.c
stdinredir1.c stdinredir2.c whotofile
root@ziggy-virtual-machine:~/unix_linux/chapter10# cat userlist
root tty7 2021-10-28 08:24 (:0)
*/
管道编程
使用管道来连接一个进程的输出和另一个进程的输入
who|sort
/*
root@ziggy-virtual-machine:~/unix_linux/chapter10# who|sort
root tty7 2021-10-28 08:24 (:0)
*/
#include <unistd.h>
int pipe(int pipefd[2]);
// 返回-1发生错误,0成功
//pipefd[0]为读取端 pipefd[1]为写入端(这是两个文件描述符)
//管道调用也使用最低有效文件描述符

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#define BUFSIZE 100
int main()
{
int pipefd[2];
int len;
char buf[BUFSIZE];
if(pipe(pipefd)==-1)
{
perror("could not make pipe");
exit(1);
}
printf("got a pipe,the file edscriptors:{%d %d}\n",pipefd[0],pipefd[1]);
//从标准输入读入数据,写入管道,从管道中读出,写出到标准输出
while(fgets(buf,BUFSIZE,stdin))
{
len = strlen(buf);
if(write(pipefd[1],buf,len)!=len)
{
perror("writing to pipe");
break;
}
for(int i = 0;i<len;i++)
{
buf[i] = 'X';
}
len = read(pipefd[0],buf,BUFSIZE);
if(len==-1)
{
perror("reading from pipe");
break;
}
if(write(1,buf,len)!=len)
{
perror("writing to stdout");
break;
}
}
return 0;
//由于所有的数据都以buf为缓冲,所以在管道读取端读取数据到buf
// 时,最好先将其中的数据覆盖为其他数据(抹除)以体现管道写入读出的数据
}

使用fork来管理管道
一个进程创建了管道后,它拥有这个管道的两端的连接,这个进程fork创建子进程时,子进程也会得到这两个连接,所以父子进程都可以管道读写数据
最好是一个进读的时候,另一个进程写数据

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include<pthread.h>
#define CHILD_MESSGE "I want a cookie\n"
#define PAR_MESSGE "testing\n"
#define oops(m,x) { perror(m);exit(x); }
int main()
{
int pipefd[2],len,read_len;
char buf[BUFSIZ];
if(pipe(pipefd)==-1)
{
oops("can not get a pipe",1);
}
switch (fork())
{
case -1:
oops("can not fork",2);
break;
case 0:
len = strlen(CHILD_MESSGE);
while(1)
{
if(write(pipefd[1],CHILD_MESSGE,len)!=len)
{
oops("write pipe",3);
}
sleep(5);
}
break;
default://父进程
len = strlen(PAR_MESSGE);
while(1)
{
if(write(pipefd[1],PAR_MESSGE,len)!=len)
{
oops("write",3);
}
sleep(1);
read_len = read(pipefd[0],buf,BUFSIZ);
if(read_len<=0)
{
break;
}
write(1,buf,read_len);
}
}
return 0;
}

学习的时候喜欢用Markdown做记录,存货已经堆满文件夹了

浙公网安备 33010602011771号