文件系统:磁盘、分区的组织形式
cd /
/home:用户目录,存放普通用户相关文件
/proc:进程,此目录的数据在内存中,包含进程等信息,如系统核心,外部设备,网络状态等
/mnt: Mount,用于挂载各种文件系统,如: U盘、SD卡等
/tmp: Temp,存放临时文件。一般用户或正在执行的程序临时存放文件的目录,任何人都可以访问,不能存放重要的数据
/etc: 系统所有的配置文件都在该目录下
/usr: User,应用程序存放目录
/lib: Library,存放系统的函数库
/bin:Binary,存放常用的系统命令。
/sbin: System Binary,存放系统管理员 root 使用的命令
来源
cd /dev:设备相关信息
lp:打印池,一个打印池可以对应一个打印机
磁盘设备:sda、sda1、sda2,磁盘插入后就会出现
stderr:标准错误设备,文件标识符为2
stdin:标准输入设备,文件标识符为0
stdout:标准输出设备,文件标识符为1
01.Linux文件系统
磁盘(中分区)组织文件的方法
001.创建文件系统
- 识别硬盘
- 双击虚拟机参数中的硬盘→添加→…→开机
- 硬盘位置:
/dev
硬盘命名规则:
– hd(IDE),比如hda,hdb;
– sd(SCSI),比如sda,sdb,sdc
- 查看磁盘基本信息:fdisk -l
分区命名规则:
– sda1:主分区,引导分区
– sda2:扩展分区
– sda5~:逻辑分区
- 给硬盘分区:fdisk <磁盘标志符>
fdisk /dev/sdb
– 输入m获得帮助
– 输入l获得分区类型介绍
– 输入n添加新分区:
可以看到当前硬盘MBR主分区表空闲
– 选p,一路回车,使整块硬盘为一个主分区
– 写入更改并退出
– 查看/dev
- 给分区格式化,创建文件系统:mkfs(make file system)
mkfs -t <文件系统类型> <分区名称>
Linux常见分区格式:
– ext2 支持反删除
– ext3 开始支持大文件(32768G,一个块8K才支持),支持日志(log)文件(有挂载开机自检)
– ext4 ext3的改进,没有日志文件了。不支持反删除
mkfs -t ext4 /dev/sdb1 对第二块硬盘的第一个分区创建文件系统
- 绑定(挂载)目录
Linux中的根目录以外的文件要想被访问,需要将其“关联”到根目录下的某个目录来实现,这种关联操作就是“挂载”,这个目录就是“挂载点”,解除次关联关系的过程称之为“卸载”
– mkdir /disk_2
– mount /dev/sdb1 /disk_2
– cd /disk_2、ls -al
– fdisk -l
– 查看磁盘基本信息df -l
可以看到每一个分区对应一个挂载点,分区表占了一点空间
– 卸载(解挂载):umount <挂载点>,然后目录和磁盘就解除关系了
002.文件与目录操作
- 权限位10位
ls -l | more
- 第一位:描述文件类型
- 普通文件
c char,字符文件(串行)
b block,块文件(并行)
l link,链接文件,一种特殊的快捷方式
p pipe,管道文件
s socket,套接字文件
d directory,文件夹(目录) - 第二~十位
2~4:属主(owner)权限rwx
5~7:属组(group)权限
8~10:非同组用户(other)权限
关于用户权限:
– 可执行权限全部去掉,属主和root用户都无法执行
– 读写权限全部去掉,文件属主无权读写,root用户依然可以
- 用户权限设定(由文件属主或root用户设定)
chmod <权限取值> <文件>
按权限取值分类:- 3位8进制数,直接设置权限,每位对应一类用户权限,用1表示有权限,0表示无权限
比如chmod 755 abc表示abc权限位为rwx r-x r-x - u/g/o +/- r/w/x ,增加减少权限,u:user;g:group;o:other
可以看到不可执行的文件被授予可执行权限后也被标记为绿色,但依然不可执行 - 文件的链接
- 硬链接:ln <原文件> <链接文件>,本质上是原文件的复制,原文件不存在硬链接文件依然有效
- 软连接:ln -s <原文件> <链接文件>,本质上就是快捷方式,原文件不在原位置后链接文件会报错,除非恢复原文件或重新创建快捷方式。作用就是可以把链接文件放到文件系统中的任意位置
- 3位8进制数,直接设置权限,每位对应一类用户权限,用1表示有权限,0表示无权限
02.文件操作相关命令
- 路径
- 绝对路径:从根到文件名称 /…/…/…/filename
- 相对路径:相对于当前路径的路径
- ./ 当前路径 ./open open.c
- . ./ 当前父路径
其中的cp ./file/open open1就是在/home/sonya/code目录拷贝子目录file下的open文件到当前目录,为open1
- $HOME 相当于Windows下的PATH路径参数,是当前用户家目录的默认值,即/home/txp
- 查看文件
ls -l 以长格式显示 -a显示隐藏文件
分别是①文件属性、②节点(子目录)个数(如果一个文件不是目录,此时这一字段表示这个文件所具有的硬链接数)、③文件(目录)拥有者、④文件(目录)拥有者所在的组、⑤文件所占用的空间(以字节为单位)、⑥文件(目录)最近访问(修改)时间、⑦文件名
ls -la
vim .bash_history
可以看到记录的是从系统登录以来以root身份执行的命令,用history命令可以查看包括普通用户的命令
vim .bash_profile
vim .bashrc
指定了系统执行命令默认的查找路径 - 路径相关
cd
mkdir
rmdir:删除目录(目录必须为空)
rm:删除文件
rm -r:删除一个非空的目录,rm -r /是最危险的命令 - 环境变量
PATH:指定一些默认目录,其下的命令可以直接执行,不用指定路径,系统会自动去这些目录找
显示环境变量echo $PATH
which:查看命令位置
- 文件操作
cp,mv,rm(不支持反删除)
通配符:*指一切内容,?是单一符号的替换 - 可执行文件
Linux没有像Windows那样的文件类型,后缀只是方便识别,能否执行要看权限 - 查看文本文件
- 用软件查看,vi编辑器、gedit等
- 用cat,head,tail等命令查看
- 创建文本文件
echo “abcabcabc”>test.log 覆盖写,>为定向符
echo “xyzxyzxyz”>>test.log 追加写 - 查看
- cat
cat /var/log/messages
cat -n messages
cat messages | more分页显示 - head messages
- tail messages
- cat
- 掩码umask
– root用户为022,普通用户为002
– 可执行文件最高权限位默认为777,文本文件为666
- 查找文件
– which:求文件的绝对路径(在$PATH路径范围内查找)
– whereis:和which相比多了通配符查找
– find:find <查找位置> -name <文件名> 能指定查找位置
03.相关程序设计
-
文件读写
系统函数:open/creat,read,write,close- open/creat函数:返回文件句柄(用来读写、关闭)
– man open
flags如果只是读写、追加的形式,不需要第三个参数,如果是创立的形式,则要第三个参数;mode_t是创立的文件的权限说明。open三参数和creat函数效果等同
flags(打开模式):
— O_RDONLY只读
— O_WRONLY只写
— O_RDWR可读写
— O_APPEND追加写
— O_CREAT创建
— O_TRUNC截断(以前的内容全部放弃)
mode_t(权限模式):S_I<权限><对象> S_I是引导词,其后占4位;或者用3个八进制数的权限位代替
— 权限:R,W,X,RWX
— 对象:USR,GRP,OTH,U,G,O
— 比如说
0是八进制标志,10是权限引导 - close函数
close(句柄)
– 代码#include <unistd.h> //Linux标准头文件 #include <stdio.h> //标准输入输出 #include <stdlib.h> //标准库文件 #include <fcntl.h> //文件控制头文件 #include <errno.h> //出错处理头文件 #include <string.h> //将错误信息转换为字符串输出 #include <sys/types.h> //在此头文件中定义了一种转换,将int等常见类型转换为有字面含义的类型,这种类型方便理解 int bail(const char*str) { perror(str); //程序出错时打印用户提供的错误说明,一定要有 printf("%s\n",strerror(errno)); //打印错误代码 当系统出错时会自动生成整数errno错误说明,strerror函数返回错误代码 exit(-1); } int main(int argc,char** argv) //可以有0~3个参数。argc记录当前命令的参数个数,argv是命令的字符串内容(char*表示不定长字符串,char**表示不定长字符串数组,比如说cp abc.txt test.log命令,argv[1]=abc.txt) { int fd=-1; //文件句柄 fd=open(argv[1],O_RDONLY); //系统函数int open(const char *pathname, int flags);以只读模式打开文件 if(fd<0) bail("Open file error.\n"); printf("fd=%d\n",fd); close(fd); return 0; }
-
read函数
在C中,有fgets函数,文件读取结束标志EOF;而read函数只有读操作,先读到缓冲区中,在对缓冲区中的内容进行操作、分析,行结束判断只能靠用户编码实现
– man 2 read(read函数说明,而不是命令)
int read(int fd,void* buf,int len);
返回值:- -1:读取失败
- 0:没有读取到数据
- n(1~len):读取到的字节数,若恰好为len,不一定读完;若在0到len-1之间,则读完了
– 代码:
#define LEN 512 //定义一次要读取的字节数 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { int fd=-1; char buf[LEN]={0}; //定义一个缓冲区,保存读取的内容 int rdret=-1; //read的返回值 fd=open(argv[1],O_RDONLY); if(fd<0) bail("Open file error.\n"); printf("fd=%d\n",fd); rdret=read(fd,buf,sizeof(buf)); while(rdret>0) { printf("%s",buf); memset(buf,0,sizeof(buf)); //读到最后有可能少于LEN,缓冲区覆盖不全,出现乱码。所以字符数组用了就清空 rdret=read(fd,buf,sizeof(buf)); } if(rdret<0) { close(fd); //打开的句柄不关掉不能退出的 bail("read file error."); } close(fd); //rdret=0直接退出 return 0; }
-
write函数
将缓冲区的内容写到句柄中
– man 2 write
ssize_t write(int fd, const void *buf, size_t count);
— const:防止缓冲区被用户操作改变,增加程序的安全性
— size_t:期望写入的字节(ascii)个数
— ssize_t:实际写入的长度,0表示没有写,-1表示写入失败,0~count表示实际写入的字符个数。有没有写完不是靠返回值,而是看用户是否给输入了,read读完了就结束了
– 代码:将从键盘中键入的内容写入文件中,键入完毕文件也自动关闭#define LEN 512 //定义一次要读取的字节数 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { int fd=-1; char buf[LEN]={0}; //定义一个缓冲区,保存读取的内容 int wtret=-1; //read的返回值 fd=open(argv[1],O_WRONLY | O_APPEND); //句柄为附加写,针对已经存在的文件 //针对要写入的文件不存在 //fd=open(argv[1],O_CREAT | O_RDWR,S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); if(fd<0) bail("Open file error.\n"); printf("fd=%d\n",fd); printf(">"); //表示这是一个输入 gets(buf); //从键盘得到一个字符串输入到缓冲区 while(strcmp(buf,"quit")!=0) //判断输入是否结束 { wtret=write(fd,buf,strlen(buf)); //输入都以字符接收的,所以用strlen计算长度 if(wtret<0) { close(fd); bail("Write file error.\n"); } //往下为下一次循环作准备 memset(buf,0,sizeof(buf)); //字符串操作前缓冲区要清空 printf(">"); gets(buf); } close(fd); //rdret=0直接退出 return 0; }
– 代码:自己写个copy命令
— 打开源文件
— 创建目标文件
— 读取源文件
— 写入目标文件
— 关闭句柄,退出#define LEN 512 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { int rdfd=-1; int wtfd=-1; char buf[LEN]={0}; int rdret=-1; int wtret=-1; //打开源文件 rdfd=open(argv[1],O_RDONLY); if(rdfd<0) bail("Open file error.\n"); //创建目标文件 wtfd=open(argv[2],O_CREAT | O_WRONLY,010755);//有可执行权限,一般以二进制形式处理 if(wtfd<0) { close(rdfd); bail("Create file error."); } //rdfd、wdfd都有了 //读取源文件 rdret=read(rdfd,buf,sizeof(buf));//从读句柄中读 while(rdret>0) { //写入目标文件 wtret=write(wtfd,buf,rdret);//将缓冲区中的实际内容写入句柄,而不是以缓冲区的大小或strlen(以/0结束计算,有多个字符串只计算第一个) if(wtret<rdret) { close(rdfd); close(wtfd); bail("Write file error."); } memset(buf,0,sizeof(buf)); rdret=read(rdfd,buf,sizeof(buf)); } if(rdret<0) { close(rdfd); close(wtfd); bail("read file error."); } //rdret=0,关闭句柄,退出 close(rdfd); close(wtfd); return 0; }
./copy open myopen
问题:权限不一样,要读取权限;读文件不存在、写文件存在、读文件没有读权限等
- open/creat函数:返回文件句柄(用来读写、关闭)
-
文件控制
-
access函数:访问函数,看文件是否存在、获得文件的读写权限
man 2 access
int access(const char *pathname, int mode);
– 返回值:0为测试成功,-1为失败
– pathname:文件名
– mode:测试权限,多个条件都要满足用 | 符号连接
— R_OK:读可以
— W_OK:写可以
— X_OK:执行可以
— F_OK:文件存在
vimx access.c#define LEN 512 //定义一次要读取的字节数 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { int ret=-1; ret=access(argv[1],F_OK);//文件名由参数argv[1]决定 if(ret<0) printf("File not exist.\n"); else printf("File exist.\n"); return 0; }
读:
ret=access(argv[1],R_OK); if(ret==0) fd=open();
写:
ret=access(argv[1],F_OK); if(ret<0) fd=creat(); else printf("overwrite?");
-
stat函数:读取权限位
man 2 stat
– sys/types.h中类型的宏定义转换,比如uid_t转为int
– sys/stat.h中定义了stat结构体
– int stat(const char *pathname, struct stat *statbuf);
将文件名为pathname的文件的状态读入结构体中。引用结构体用的是指针,在调用时要用取地址符&
struct stat st;
ret=stat(文件名,&st);
返回值:-1,失败
st返回值:
– st.st_size 文件大小
– st.st_mode 文件类型、权限
– st.st_?time 时间相关参数(秒为单位,用ctime转换)
– st.mode:- st.mode&S_IFMT
- 宏函数
S_ISDIR
S_ISREG
S_ISLNK
S_IFIFO
S_ISCHR
S_ISOCK
代码:复制源文件权限到目标文件
#define LEN 512 //定义一次要读取的字节数 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/stat.h>//stat头文件 #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { int ret=-1; int sfd=-1; int dfd=-1; struct stat bs; memset(&bs,0,sizeof(bs)); ret=stat(argv[1],&bs);//读取源文件状态 dfd=creat(argv[2],bs.st_mode);//以源文件权限位创建目标文件 close(dfd);//啥都不做 return 0; }
-
-
目录操作
dir,opendir,closedir,readdir
步骤:打开目录、读取目录信息、关闭目录-
打开目录(要头文件dirent.h)
打开目录返回的句柄不是一个整数,而是一个指针:
DIR* opendir(目录名称)
– 成功:返回地址
– 失败:返回NULL -
关闭目录
closedir(DIR* 目录指针)
– 成功:0
– 失败:-1 -
读取目录信息
怎么读取信息、信息放到哪去?
struct dirent* readdir(DIR* 指针);
– 成功:返回结构体指针(每次读完的信息返回到这个结构体)
– 失败:NULL
– 方式:循环自动读取
man 3 readdir(3是传统的)
主要就是为了得到那两个分量#define LEN 512 //定义一次要读取的字节数 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <dirent.h>//与目录操作有关的头文件 #include <sys/stat.h> #include <sys/types.h> int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { DIR* dp=NULL;//定义一个目录指针 struct dirent* dir=NULL;//定义一个结构体指针,指向读取的内容 dp=opendir(argv[1]); if(dp==NULL) bail("open dir error."); //readdir(dp);//不重复读,读取的是目录的第一个文件 while((dir=readdir(dp))!=NULL) { if(dir->d_name[0]=='.')//如果文件名以.引导,不可见;.为当前目录,..为父目录,这两个在目录中一定存在 continue; if(dir->d_type==DT_REG) //各种目录操作的位置 printf("Regular file:%s\n",dir->d_name); if(dir->d_type==DT_DIR) printf("Dir file:%s\n",dir->d_name); } closedir(dp); return 0; }
./readdir .
./readdir $HOME -
创建目录函数
man 2 mkdir
#include <sys/stat.h>:权限参数可以从<stat.h>中的结构体得到
int mkdir(const char *pathname, mode_t mode);
– 返回值:成功:0,失败:-1 -
切换目录函数
chdir(新目录名称)
– 代码:给定源目录,遍历目录,创建一个新目录,将源目录中子项添加到新目录中去
— 假定:仅复制一级目录,不进入子目录,即只创建,不复制
— cpdir(source,dest):将源目录的子项复制到目标目录中去- 可行性:source要存在、可读,dest不存在,可写;
- 步骤:
– 读source状态,作为dest的状态创建目标目录
– source:readdir;chdir(dest)
– dest:mkdir/creat;chdir(source)
– 关闭目录
#define LEN 512 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <dirent.h>//目录头文件 #include <sys/stat.h>//文件状态 int bail(const char*str) { perror(str); printf("%s\n",strerror(errno)); exit(-1); } int main(int argc,char** argv) { char sdir[1024]={0};//源目录路径 char ddir[1024]={0};//目标目录路径 char sname[1024]={0};//源文件名 char dname[1024]={0};//目标文件名 int ret=-1; struct dirent* dir=NULL;//读取的目录中每一条(目录子项)的信息,用readdir返回 DIR* dp=NULL;//指针指向目录的指针,用opendir返回 struct stat sb;//存放源目录的权限状态 int fd=-1;//creat返回的句柄 if(access(argv[1],F_OK)<0)//第一件事看源目录是否存在 bail("Source directory not exist."); if(access(argv[1],R_OK)<0) bail("Perssion denied."); if(access(argv[2],F_OK)==0) bail("Dest directory exist."); getcwd(sdir,sizeof(sdir));//获取源目录(当前路径)绝对路径,get current workspace directory memset(&sb,0,sizeof(sb)); stat(argv[1],&sb);//获得源目录的权限 if(mkdir(argv[2],sb.st_mode)<0)//创建目标路径 bail("Creat dest directory error."); chdir(argv[2]); getcwd(ddir,sizeof(ddir));//为什么不直接是argv[2]?因为那可能是相对路径 chdir(sdir); dp=opendir(argv[1]); while((dir=readdir(dp))!=NULL) { if(dir->d_name[0]=='.') continue; else if(dir->d_type==DT_DIR) { memset(&sb,0,sizeof(sb)); stat(dir->d_name,&sb);//根据源文件的权限模式创建目标文件 chdir(ddir); mkdir(dir->d_name,sb.st_mode); chdir(sdir); continue; } else//目录用mkdir,其他用creat { memset(&sb,0,sizeof(sb)); stat(dir->d_name,&sb); chdir(ddir); fd=creat(dir->d_name,sb.st_mode); close(fd); chdir(sdir); continue; } } closedir(dp); }
效果:
-