1、系统文件读写
任务描述:
编写程序,读取linux系统文件"/etc/passwd"内容,并输出到屏幕上.同时在程序目录新建文件passwd,将"/etc/passwd"系统文件内容复制到新建的passwd中
main.c:
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #define N 1000 int main(void) { int fp1,fp2,num; char *file1,*file2; char buf[N]; file1="/etc/passwd"; file2="passwd"; if(( fp1=open(file1,O_RDONLY))==-1){ printf("can‘t open %s\n",file1); return 1; } if((fp2=open(file2,O_CREAT|O_WRONLY))==-1){ printf("can‘t create %s\n",file2); return -1; } while((num = read(fp1,buf,N))>0){ printf("%s\n",buf); if(write(fp2,buf,num) == -1 ){ printf("can‘t write to %s\n",file2); return -1; } } close(fp1);close(fp2); return 0; }
2、文件内容统计操作
任务描述:
- 给定系统文件filesystem.manifest(dpkg-query -W --showformat=‘${Package} ${Version}\n‘>filesystem.manifest)
- 目前文件每一行格式为"软件包名 版本号",要求输出每一行的格式为"软件包名"(去除版本号相关信息)到文件filesystem.manifest.name
- 在第一步基础上,进一步去除换行符号,使整个文件的输出为一行(不同包名用空格分割),格式为"软件包1 软件包2 软件包3...",输出到文件filesystem.manifest.name.oneline
- 目前每一行格式为"软件包名 版本号",找到文件中软件包名有"ubuntu"字样的软件包,并直接输出其个数(不能人工查找,需直接输出其个数)
相关知识:
①cut 命令可以从一个文本文件或者文本流中提取文本列。
命令用法:
cut -b list [-n] [file ...]
cut -c list [file ...]
cut -f list [-d delim][-s][file ...]
上面的-b、-c、-f 分别表示字节、字符、字段(即 byte、character、field);
list 表示-b、-c、-f 操作范围,-n 常常表示具体数字;
file 表示的自然是要操作的文本文件的名称;
delim(英文全写:delimiter)表示分隔符,默认情况下为 TAB;
-s 表示不包括那些不含分隔符的行(这样有利于去掉注释和标题)
上面三种方式中,表示从指定的范围中提取字节(-b)、或字符(-c)、或字段(-f)。
②xargs 是一条 Unix 和类 Unix 操作系统的常用命令。它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。
例如,下面的命令:
rm `find /path -type f`如果 path 目录下文件过多就会因为“参数列表过长”而报错无法执行。但改用 xargs 以后,问题即获解决。
find /path -type f -print0 | xargs -0 rm
本例中 xargs 将 find 产生的长串文件列表拆散成多个子串,然后对每个子串调用 rm。这样要比如下使用 find 命令效率高的多。
find /path -type f -exec rm ‘{}‘ \;
上面这条命令会对每个文件调用"rm"命令。当然使用新版的"find"也可以得到和"xargs"
命令同样的效果:
find /path -type f -exec rm ‘{}‘ +
xargs 的作用一般等同于大多数 Unix shell 中的反引号,但更加灵活易用,并可以正确处理输入中有空格等特殊字符的情况。对于经常产生大量输出的命令如 find、locate 和 grep 来
说非常有用。
#!/bin/bash dpkg-query -W --showformat=‘${Package} ${Version}\n‘>filesystem.manifest cat filesystem.manifest|cut -d ‘ ‘ -f 1 >filesystem.manifest.name cat filesystem.manifest.name|xargs >filesystem.manifest.name.oneline cat filesystem.manifest|grep "ubuntu"|wc -l #-d ‘ ‘表示以空格为字符 -f 1表示输出第一列 #xargs表示将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题
3、文件描述符获取
任务描述:
- 打印输入设备,输出设备,标准错误输出设备的文件描述符
- 任意打开一个存在文件a,打印其文件描述符
- 不关闭文件a,定义新的文件描述符重新打开a,打印其文件描述符
- 关闭文件a,定义新的文件描述符重新打开a,打印文件描述符
相关知识:
任何打开的文件都将被分配一个唯一标识该打开文件的文件描述符,为一个大于等于 0的整数。系统启动后,默认打开的文件流有标准输入设备(STDIN)、标准输出设备(STDOUT)和标准错误输出设备(STDERR),其文件描述符分别为 0、1、2。以后打开的文件的文件描述符分配依次增加。使用 fileno()函数可以返回一个流对应的文件描述符。
main.c:
#include <stdio.h> int main() { FILE *fp1,*fp2,*fp3,*fp4; int fd; fd=fileno(stdin); printf("stdin is :%d\n",fd); fd=fileno(stdout); printf("stdout is :%d\n",fd); fd=fileno(stderr); printf("stderr is :%d\n",fd);
printf("open file and open again before it closed\n"); fp1=fopen("test.c", "r"); fd=fileno(fp1); printf("main.c is %d\n", fd); fp2=fopen("test.c","r"); fd=fileno(fp2); printf("main.c is %d\n",fd); fclose(fp1);fclose(fp2);
printf("open file and open again after it closed\n"); fp3=fopen("test.c","r"); fd=fileno(fp3); printf("main.c is %d\n",fd); fclose(fp3); fp4=fopen("test.c","r"); fd=fileno(fp4); printf("main.c is %d\n",fd); return 0; }
4、检测文件读写权限
任务描述:
- 检测文件当前读写权限,如果文件具有读权限,则打印可读信息,如果有写权限,则打印可写信息,否则返回错误信息
- 用fcntl函数实现
相关知识:
fcntl 功能描述:根据文件描述词来操作文件的特性。
文件控制函数:fcntl -- file control
函数原型:
#include <fcntl.h>;
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
描述: fcntl()针对(文件)描述符提供控制.参数 fd 是被参数 cmd 操作(如下面的描述)的描述符。针对 cmd 的值,fcntl 能够接受第三个参数 int arg。
fcntl 函数有 5 种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD 或 F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL 或 F_SETFL).
4.获得/设置异步 I/O 所有权(cmd=F_GETOWN 或 F_SETOWN).5.获得/设置记录锁(cmd=F_GETLK,F_SETLK 或 F_SETLKW).
main.c:
#include<stdio.h> #include<fcntl.h> int main(void) { FILE *fp; int val,acc,a; if(( fp=fopen("test.c","r+")) == NULL){ printf("can‘t open the file\n"); return 1; } a=fileno(fp); val=fcntl(a,F_GETFL,0);//获得文件状态标记 acc=val&O_ACCMODE;//把不是关于r、w、x的文件状态标记屏蔽掉 if (acc==O_RDONLY) printf("read only\n"); if (acc==O_WRONLY) printf("write only\n"); if (acc==O_RDWR) printf("read write\n"); fclose(fp); return 0; }
5、锁定/解锁文件
任务描述:
- 用fcntl函数实现锁定文件test_lock的两个区域,锁定类型均为以文件开头为锁定的起始位置,区域1锁定为供读取用,起始偏移量为10,长度为20;区域2锁定为供写入用,起始偏移量为40,长度为10.
- 当进程锁定文件指定区域时,输出"process locking file",停顿10秒钟,关闭文件描述符,输出"process closing file"
- 使用flock数据结构,使用fcntl函数
相关知识:
Linux 支持的文件锁技术主要包括劝告锁(advisory lock)和强制锁(mandatory lock)这两种。此外,Linux 中还引入了两种强制锁的变种形式:共享模式强制锁(share-mode mandatory lock)和租借锁(lease)。
在 Linux 中,不论进程是在使用劝告锁还是强制锁,它都可以同时使用共享锁和排他锁(又称为读锁和写锁)。多个共享锁之间不会相互干扰,多个进程在同一时刻可以对同一个文件加共享锁。但是,如果一个进程对该文件加了排他锁,那么其他进程则无权再对该文件加共享锁或者排他锁,直到该排他锁被释放。所以,对于同一个文件来说,它可以同时拥有很多读者,但是在某一特定时刻,它只能拥有一个写者
flock 函数用于实现对文件的锁定和解锁操作。此函数只能锁定整个文件,不能锁定某个区域。要锁定某个区域,则需要使用 fcntl()函数。
int fcntl(int fd, int cmd, struct flock *lock);参数lock指针为flock 结构指针,定义如下:
struct flock{
short int l_type;
short int l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
};
l_type 有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定
l_whence 也有三种方式:
SEEK_SET 以文件开头为锁定的起始位置。
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置。
l_start 表示相对l_whence位置的偏移量,两者一起确定锁定区域的开始位置。
l_len表示锁定区域的长度,若果为0表示从起点(由l_whence和 l_start决定的开始位置)开始直到最大可能偏移量为止。即不管在后面增加多少数据都在锁的范围内。
main.c:
#include<stdio.h> #include<fcntl.h> int main(void) { int re,fd; fd=open("test_lock",O_RDWR|O_CREAT,0644); struct flock z1; z1.l_type=F_RDLCK;//建立一个供读取用的锁定 z1.l_whence=SEEK_SET;//以文件开头为锁定的起始位置 z1.l_start=10; z1.l_len=20; printf("Process %d locking file\n",getpid()); if((re=fcntl(fd,F_SETLK,&z1))==-1){ printf("failed to lock z1"); } struct flock z2; z2.l_type=F_WRLCK;//建立一个供写入用的锁定 z2.l_whence=SEEK_SET;//以文件开头为锁定的起始位置 z2.l_start=40; z2.l_len=10; if((re=fcntl(fd,F_SETLK,&z2))==-1){ printf("failed to lock z2"); } sleep( 10); printf("Process %d closing file\n",getpid()); close(fd); return 0; }
6、使用 chmod()函数修改文件权限
任务描述:
- 首先使用touch命令新建3个临时文件:test1,test2,test3
- 查看最原始文件权限情况(是否为拥有者可读写,所有其他人可读)
- 设置test1为拥有者可读写,同组用户可读写,其他人可读;test2为拥有者可读写,同组用户可读写可执行,其他人可读;test3为拥有者可读写,同组用户可写不可读、其他用户可写不可读
- 采用stat数据结,chmod函数
- 输出修改权限前和修改权限后的文件属性
相关知识:
文件权限知识:
Linux 系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作。
文件或目录的访问权限分为只读,只写和可执行三种。以文件为例,只读权限表示只允许读其内容,而禁止对其做任何的更改操作。可执行权限表示允许将该文件作为一个程序执行。文件被创建时,文件所有者自动拥有对该文件的读、写和可执行权限,以便于对文件的阅读和修改。用户也可根据需要把访问权限设置为需要的任何组合。有三种不同类型的用户可对文件或目录进行访问:文件所有者,同组用户、其他用户。
所有者一般是文件的创建者。所有者可以允许同组用户有权访问文件,还可以将文件的访问权限赋予系统中的其他用户。在这种情况下,系统中每一位用户都能访问该用户拥有的文件或目录。
每一文件或目录的访问权限都有三组,每组用三位表示,分别为文件属主的读、写和执行权限;与属主同组的用户的读、写和执行权限;系统中其他用户的读、写和执行权限。当用 ls -l 命令显示文件或目录的详细信息时,最左边的一列为文件的访问权限。
main.c:
#include<stdio.h> #include<sys/stat.h> #include<unistd.h> int main(void) { chmod("test01",S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); chmod("test02",S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH); chmod("test03",S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH); return 0; }