Linux文件管理
1 文件系统目录结构
1.1 文件系统目录结构
- 文件和目录被组织成一个单根倒置树结构
- 文件系统从根目录下开始, 用"/"表示
- 根文件系统(rootfs): root filesystem
- 标准Linux文件系统(如: ext4), 文件名称大小写敏感
- 以.开头的为隐藏文件
- 路径分隔符为/
- 文件名最长255个字节
- 包含路径在内, 文件名称最长4095个字节
- 蓝色: 目录, 绿色: 可执行文件, 红色: 压缩文件, 浅蓝色: 链接文件, 灰色: 其他文件
- 文件名和目录名: 除了斜杠和NUL, 所有字符都有效. 但使用特殊字符的目录名和文件名不推荐使用, 有些字符需要用引号来引用
- Linux的文件系统分层结构: FHS
Linux文件中看到的数据有两种存储方式:
因为Linxu一切接文件, 所以在系统中看到的文件和数据可能是内存数据和文件, 也可能是硬盘数据和文件
外存-硬盘文件-存储器-辅助存储
内存-主存储器
1.2 常见的文件系统目录功能
- bin 二进制程序
- boot 引导程序 - 内核
- dev 设备
- etc 配置文件
- home 用户数据
- root 根用户数据
- run 运行临时数据
- sbin 管理员运行程序
- tmp 临时目录
- usr 操作系统相关文件, 小Linux系统
- var 变化数据, 日志
1.3 应用程序的组成部分

1.4 CentOS7以后版本目录结构变化
Centos7以后, 两个存放内容相同的目录, 会被做成软链接形式:
/bin->/usr/bin
/sbin->/usr/sbin
/lib->/usr/lib
/lib64->/usr/lib64
1.5 Linux下的文件类型
- -: 普通文件
- d: 目录文件Directory
- b: 块设备Block, 以块的方式进行数据读写, 典型的就是磁盘设备
- c: 字符设备Character, 以字符的方式进行读写, 比如键盘设备
- l: 符号链接文件Link
- p: 管道文件Pipe, 实现两个进程间互相通讯, 但是是单项通讯, 一个程序发一个程序读, 两个程序不能同时读或者发
- s: 套接字文件Socket, 实现两个进程间双向通讯, 两个进程可以同时进行读和写
2 文件操作命令
2.1 显示当前工作目录
每个Shell和系统进程都有一个当前的工作目录: Current Working Directory
显示当前所在目录命令: pwd
[22:17:41 root@demo-c8 ~]#pwd
/root
2.2 绝对路径和相对路径
-
绝对路径: 从根/开始描述
-
相对路径: 从当前工作目录开始描述
2.3 更改目录
- 切换到某一个用户的家目录
cd ~USERNAME
- 回到自己的家目录
cd
- 回到上一个目录
# 只能回到上一个目录, 上一次目录记录在OLDPWD变量, 当前目录记录在PWD变量
cd -
[20:32:16 root@Anabile-1 /]#cd root
[20:32:25 root@Anabile-1 ~]#echo $PWD
/root
[20:32:29 root@Anabile-1 ~]#echo $OLDPWD
/
[20:32:32 root@Anabile-1 ~]#cd -
/
[20:32:56 root@Anabile-1 /]#cd -
/root
[20:32:57 root@Anabile-1 ~]#cd -
/
- 进入当前位置的上一级目录
[22:37:07 root@demo-c8 ~]#cd /etc/profile.d
[22:37:14 root@demo-c8 /etc/profile.d]#cd ..
[22:37:15 root@demo-c8 /etc]#cd ..
[22:37:16 root@demo-c8 /]#cd ..
[22:37:17 root@demo-c8 /]#cd ..
[22:37:20 root@demo-c8 /]# # 最外层就是/目录
- 进入当前目录
cd .
-
当前目录用.表示, 上层目录用..表示
-
basename: 只取文件名
-
dirname: 只取文件所在的目录, 从/开始取
-
basename+dirname即为一个文件的完整路径
[20:26:39 root@Anabile-1 ~]#basename /root/anaconda-ks.cfg
anaconda-ks.cfg
[20:29:38 root@Anabile-1 ~]#dirname /root/anaconda-ks.cfg
/root
2.4 列出目录内容
常见选项:
-R: 目录递归, 默认只显示当前目录内的内容, 递归后, 会进入到每个目录, 进行数据显示
-1: 让文件名分行显示, ls -1
-S: 按照文件大小, 从大到小排序, -r -S, 从小到大排序
-t: 按照mtime排序, 默认从新到旧
-u: 配合-t选项, 按照atime时间, 从新到旧排序
-U: 按照目录存放顺序排序
-X: 按照文件后缀排序
-r: 按照默认顺序的反向顺序排序
- ls: 仅列出文件名, 不包含隐藏文件
anaconda-ks.cfg.bak Desktop Downloads Music Public test.txt Videos
asicc.txt.txt Documents initial-setup-ks.cfg Pictures Templates 't.txt'$'\033'':Q!'
- ls -l: 列出文件详细信息
[22:44:01 root@demo-c8 ~]#ls -l
total 28
-rw-------. 1 root root 1385 Aug 15 17:06 anaconda-ks.cfg.bak
-rw-r--r--. 1 root root 7 Aug 17 12:02 asicc.txt.txt
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Desktop
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Documents
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Downloads
-rw-r--r--. 1 root root 1495 Aug 15 17:17 initial-setup-ks.cfg
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Music
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Pictures
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Public
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Templates
-rw-r--r--. 1 root root 6 Aug 17 11:55 test.txt
-rw-r--r--. 1 root root 8233 Aug 17 11:54 't.txt'$'\033'':Q!'
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Videos
- ls -l: 显示的是文件的修改时间modify
- 一个文件的三个时间属性
modify time(mtime): 内容更改
access time(atime):读文件
change time(ctime):属性,元数据变化
- 显示文件的某个时间
ls -l --time=atime FILE
ls -l --time=mtime FILE
ls -l --time=ctime FILE
[20:36:54 root@Anabile-1 ~]#ls -l --time=atime anaconda-ks.cfg # atime
-rw-------. 1 root root 1412 Aug 13 20:36 anaconda-ks.cfg
[20:36:58 root@Anabile-1 ~]#ls -l anaconda-ks.cfg # 默认显示mtime
-rw-------. 1 root root 1412 Aug 12 14:06 anaconda-ks.cfg
- 只显示隐藏文件: l.
[20:37:46 root@Anabile-1 ~]#l. # l.是别名
. .ansible .bash_history .bash_profile .cshrc .tcshrc .vimrc
.. .ansible-console_history .bash_logout .bashrc .ssh .viminfo .vimrc.0
[20:38:25 root@Anabile-1 ~]#alias l.
alias l.='ls -d .* --color=auto'
- 显示所有文件, 包含隐藏文件
[22:44:16 root@demo-c8 ~]#ls -a
. asicc.txt.txt .bash_profile .config Desktop .esd_auth .lesshst Pictures .tcshrc 't.txt'$'\033'':Q!' .Xauthority
.. .bash_history .bashrc .cshrc Documents .ICEauthority .local .pki Templates Videos
anaconda-ks.cfg.bak .bash_logout .cache .dbus Downloads initial-setup-ks.cfg Music Public test.txt .viminfo
- ls -d DIRECTORY vs ls -l DIRECTORY
-d: 显示目录本身信息, 配合通配符时, -d只会显示目录这一层, 不会继续显示子目录的内容
-l: 显示目录内部的文件内容信息
-ld: 显示目录本身详细信息
[22:51:01 root@demo-c8 ~]#ls .config # .config是一个隐藏目录
dconf evolution gconf gnome-initial-setup-done gnome-session goa-1.0 gtk-3.0 ibus pulse user-dirs.dirs user-dirs.locale yelp
[22:51:05 root@demo-c8 ~]#ls -d .config
.config
[22:51:13 root@demo-c8 ~]#ls -l .config
total 16
drwxr-xr-x. 2 root root 18 Aug 15 17:25 dconf
drwx------. 3 root root 21 Aug 15 17:19 evolution
drwx------. 2 root root 6 Aug 15 17:19 gconf
-rw-r--r--. 1 root root 3 Aug 15 17:21 gnome-initial-setup-done
drwx------. 3 root root 27 Aug 15 17:19 gnome-session
drwxr-xr-x. 2 root root 6 Aug 15 17:19 goa-1.0
drwx------. 2 root root 23 Aug 15 17:19 gtk-3.0
drwx------. 3 root root 17 Aug 15 17:16 ibus
drwx------. 2 root root 4096 Aug 15 17:19 pulse
-rw-------. 1 root root 633 Aug 15 17:19 user-dirs.dirs
-rw-r--r--. 1 root root 5 Aug 15 17:19 user-dirs.locale
drwxr-xr-x. 2 root root 22 Aug 15 17:21 yelp
[22:51:23 root@demo-c8 ~]#ls -ld .config
drwx------. 11 root root 215 Aug 15 17:21 .config
- ls -Sla: 把目录内的所有文件和目录按照大小排序, 默认从大到小
[22:51:28 root@demo-c8 ~]#ls -Sla
total 84
-rw-r--r--. 1 root root 8233 Aug 17 11:54 't.txt'$'\033'':Q!'
-rw-------. 1 root root 8090 Aug 17 21:43 .viminfo
-rw-------. 1 root root 7275 Aug 17 21:43 .bash_history
dr-xr-x---. 15 root root 4096 Aug 17 21:43 .
-rw-r--r--. 1 root root 1495 Aug 15 17:17 initial-setup-ks.cfg
-rw-------. 1 root root 1385 Aug 15 17:06 anaconda-ks.cfg.bak
-rw-------. 1 root root 310 Aug 15 17:19 .ICEauthority
dr-xr-xr-x. 18 root root 236 Aug 17 10:15 ..
drwx------. 10 root root 230 Aug 15 17:21 .cache
-rw-r--r--. 1 root root 224 Aug 16 21:14 .bashrc
-rw-r--r--. 1 root root 216 Aug 17 19:05 .bash_profile
drwx------. 11 root root 215 Aug 15 17:21 .config
-rw-r--r--. 1 root root 129 May 11 2019 .tcshrc
-rw-r--r--. 1 root root 100 May 11 2019 .cshrc
-rw-------. 1 root root 58 Aug 16 21:52 .Xauthority
-rw-------. 1 root root 37 Aug 17 20:00 .lesshst
drwx------. 3 root root 25 Aug 15 17:16 .dbus
drwx------. 3 root root 19 Aug 15 17:19 .local
drwxr-----. 3 root root 19 Aug 15 17:19 .pki
-rw-r--r--. 1 root root 18 May 11 2019 .bash_logout
-rw-------. 1 root root 16 Aug 15 17:19 .esd_auth
-rw-r--r--. 1 root root 7 Aug 17 12:02 asicc.txt.txt
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Desktop
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Documents
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Downloads
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Music
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Pictures
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Public
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Templates
-rw-r--r--. 1 root root 6 Aug 17 11:55 test.txt
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Videos
[22:57:13 root@demo-c8 ~]#ls -Slar # -r: 从小到大, 反向
total 84
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Videos
-rw-r--r--. 1 root root 6 Aug 17 11:55 test.txt
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Templates
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Public
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Pictures
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Music
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Downloads
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Documents
drwxr-xr-x. 2 root root 6 Aug 15 17:19 Desktop
-rw-r--r--. 1 root root 7 Aug 17 12:02 asicc.txt.txt
-rw-------. 1 root root 16 Aug 15 17:19 .esd_auth
-rw-r--r--. 1 root root 18 May 11 2019 .bash_logout
drwxr-----. 3 root root 19 Aug 15 17:19 .pki
drwx------. 3 root root 19 Aug 15 17:19 .local
drwx------. 3 root root 25 Aug 15 17:16 .dbus
-rw-------. 1 root root 37 Aug 17 20:00 .lesshst
-rw-------. 1 root root 58 Aug 16 21:52 .Xauthority
-rw-r--r--. 1 root root 100 May 11 2019 .cshrc
-rw-r--r--. 1 root root 129 May 11 2019 .tcshrc
drwx------. 11 root root 215 Aug 15 17:21 .config
-rw-r--r--. 1 root root 216 Aug 17 19:05 .bash_profile
-rw-r--r--. 1 root root 224 Aug 16 21:14 .bashrc
drwx------. 10 root root 230 Aug 15 17:21 .cache
dr-xr-xr-x. 18 root root 236 Aug 17 10:15 ..
-rw-------. 1 root root 310 Aug 15 17:19 .ICEauthority
-rw-------. 1 root root 1385 Aug 15 17:06 anaconda-ks.cfg.bak
-rw-r--r--. 1 root root 1495 Aug 15 17:17 initial-setup-ks.cfg
dr-xr-x---. 15 root root 4096 Aug 17 21:43 .
-rw-------. 1 root root 7275 Aug 17 21:43 .bash_history
-rw-------. 1 root root 8090 Aug 17 21:43 .viminfo
-rw-r--r--. 1 root root 8233 Aug 17 11:54 't.txt'$'\033'':Q!'
-
ls -R: 递归显示, 会进入到每个子目录内显示出所有文件, 而ls默认只显示当前目录内的内容
image.png
-
ls查看不同后缀文件时的颜色由
/etc/DIR_COLORS
和@LS_COLORS变量定义
案例: jpg类型默认显示为紫色, 我们修改为绿色

- 修改配置文件
[23:22:35 root@demo-c8 ~]#vim /etc/DIR_COLORS
# image formats
.jpg 01;32
-
退出当前终端, 重新登录
image.png
2.5 查看文件状态-stat
Linux文件包含元数据Metadata和实际数据Data
- stat: 显示文件详细属性, Metadata
- 每个文件由三个时间戳
access time: 访问时间, atime, 读取文件内容
modify time: 修改时间, mtime, 改变文件内容(数据)
change time: 改变时间, ctime, 元数据发生改变
[20:37:02 root@Anabile-1 ~]#stat anaconda-ks.cfg
File: anaconda-ks.cfg
Size: 1412 Blocks: 8 IO Block: 4096 regular file
Device: 802h/2050d Inode: 202265935 Links: 1
Access: (0600/-rw-------) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-08-13 20:36:54.310987472 +1000
Modify: 2020-08-12 14:06:15.405009322 +1000
Change: 2020-08-12 14:06:15.405009322 +1000
Birth: -
2.6 确定文件内容
2.6.1 查看文件类型 - file
文件可以包含多种类型的数据, 可以使用file
命令检查文件的类型, 然后确定适当的打开命令或使用对应的应用程序
file通过检测文件前几位, 也就是元数据来判断文件类型
- 一个普通文件包含多种类型: jpg, exe, txt等
Linux文件对后缀没有要求, 即便是二进制可执行程序改成了.txt后缀, 也可以照样执行, 但是执行时需要写上文件后缀,否则会提示找不到文件, 因为不加后缀执行,系统会认为执行的还是原来的程序文件
比如 把/usr/bin/ls 改成/usr/bin/ls.txt
那么执行命令时就要用ls.txt而不是ls
[23:25:16 root@demo-c8 ~]#ls
anaconda-ks.cfg.bak Desktop Downloads Music Public test.jpg 't.txt'$'\033'':Q!'
asicc.txt.txt Documents initial-setup-ks.cfg Pictures Templates test.txt Videos
[23:43:14 root@demo-c8 ~]#mv /usr/bin/ls /usr/bin/ls.txt
[23:43:26 root@demo-c8 ~]#ls
-bash: /usr/bin/ls: No such file or directory
[23:43:31 root@demo-c8 ~]#ls.txt
anaconda-ks.cfg.bak Desktop Downloads Music Public test.jpg 't.txt'$'\033'':Q!'
asicc.txt.txt Documents initial-setup-ks.cfg Pictures Templates test.txt Videos
[23:43:40 root@demo-c8 ~]#mv /usr/bin/ls.txt /usr/bin/ls
[23:43:58 root@demo-c8 ~]#ls
anaconda-ks.cfg.bak Desktop Downloads Music Public test.jpg 't.txt'$'\033'':Q!'
asicc.txt.txt Documents initial-setup-ks.cfg Pictures Templates test.txt Videos
-
ELF: Linux二进制可执行程序
-
软链接: symbolic link to FILE
2.6.2 Windows和Linux文本格式区别
- Windows vs Linux 文本格式区别
Windows的换行是回车+换行
实现的, 换行是光标移动到当前位置下一行的同一位置, 回车是回到当前行首. 所以Windows是先执行0d再执行0a.
Linux 只有换行,没有回车,也就是Linux换行把两件事都干了. 所以, Linux只有0a
换行 \n 0a
回车 \r 0d
-
Windows文件在Linux里用
file
命令查看会显示为CRLF line terminators
, 表示带有回车的行结束符, 因为Windows文件比Linux文件多一个\r
回车符 -
hexdump -C: 将文件内容以16进制展示
-
避免文本乱码, 需要确保文本保存和打开使用相同的编码机制. 用UTF-8保存的文本, 就要用UTF-8编码去打开, 否则会乱码
-
Windows文本格式转换成Linux格式
dos2unix
- Linux文本格式转换成Windows格式
unix2dos
-
dos2unix和unix2dos只是转换文本格式(修改0a和0d), 而不会修改文本的编码
-
在Linux上完成对Windows文本文件的转码, 从其他编码转到UTF-8
案例:
- 在Windows记事本创建文件, 保存为ANSI(GBK)编码


-
将a.txt从Windows传到Linux中, 验证打开为乱码, 因为Linux默认用UTF-8编码保存和打开文件
image.png
[23:57:03 root@demo-c8 ~]#file a.txt # ISO-8859表示的就是GBK编码
a.txt: ISO-8859 text, with CRLF line terminators
- 通过
iconv
命令, 将GBK编码文件转换为UTF-8编码

-
iconv
也可以把UTF-8转成其他编码格式

2.7 文件通配符模式-wildcard pattern
文件通配符可以用来匹配符合条件的多个文件名和目录名, 方便批量管理文件和目录.
通配符采用特定的符号, 表示特定的含义, 此特定符号称为元meta字符
通配符除了*
匹配0个或任意个字符外, 其余的通配符都是逐位匹配, 无法通过某一个通配符, 匹配一串字符
常见通配符如下:
这里的字符指的是任何人类字符, 汉字,英文,数字,标点
* 匹配零个或多个任意字符, 但是不匹配'.'开头的文件, 也就是隐藏文件和目录
? 匹配任意单个字符
[0-9] 匹配0-9内的任意一个数字
[a-z] 匹配aAbBcCdDeE...z内的任意一个字母
[A-Z] 匹配AbBcCdDeE...zZ内的任意一个字母
[wang] 匹配列表内的任意一个字符, w, a, n, g都符合要求
[^wang] 匹配列表内的所有字符以外的任意单个字符, 也就是匹配不是w, a, n, g的任意一个字符
通配符[]
匹配连续范围字母时的顺序为aAbBcC...zZ; 花括号{}
作为扩展命令时, 其顺序为ASCII顺序
书写通配符时, 只需要按照要求的模式, 把每一个字符位上的模式写出来即可, 从文件第一个字符位, 写到最后一个字符位
另外还有在Linux系统中预定义的字符类: man 7 glob, 这些字符类在使用时需要写在[]
里
[:digit:] 相当于0-9, 表示任意单个数字, 范围是0-9
[:lower:] 任意一个小写字母, 和[a-z]不等价
[:upper:] 任意一个大写字母, 和[A-Z]不等价
[:alpha:] 任意一个大写或小写字母
[:blank:] 任意一个水平空白字符
[:space:] 任意一个水平或垂直空白字符
[:punct:] 任意一个标点符号
[:print:] 任意一个可打印字符
[:cntrl:] 任意一个控制(非打印)字符
[:graph:] 图形字符
[:xdigit:] 任意一个16进制字符
- 通配符问号
?
匹配的是一个字符, 可以是英文, 也可以是中文.?
匹配的是字符个数, 不是字节个数.
UTF-8编码: 在磁盘上,一个英文单词占一个字节,byte, 一个汉字占3-4个字节,byte
- 花括号扩展, 用于创建文件和目录,
[]
中括号通配符, 用于匹配文件名或目录名 - 中括号内, 用
-
分开两个字符[字符1-字符2]
, 表示的是匹配从字符1到字符2的任意单个字符, 如果要匹配离散字符, 也就是指定的字符, 用[136aW!]
这种 - 中括号匹配连续字符时, 数字和字母可以放在一起匹配, [0-Z], 匹配0,1,2,3..9,aAbB..zZ内的任意单个字符
-
ls
显示文件就是按照顺序aAbB..zZ
显示, 如果首字母相同, 再比较第二个,,, - Linux中, 目录是以
/
结尾的, 而文件的结尾没有/
, 所以使用通配符管理文件和目录时, 可以按照结尾是否有/
来区分文件和目录
案例: 比较有无*
的功能区别, *
不显示隐藏文件和目录
ls -a * : 不显示隐藏文件和目录
ls -a: 显示隐藏文件和目录

2.8 创建空文件和刷新时间
touch可以用来创建空文件或刷新已有文件的时间戳
- 选项说明:
-a 仅改变atime和ctime
-m 仅改变mtime和ctime
-t [[CC]YY]MMDDhhmm[.ss] 指定atime和mtime的时间戳
-c 如果文件不存在, 则不予创建
- touch: 如果文件不存在,则创建文件, 如果文件存在,则刷新时间戳(三个时间都刷新), 但是不会覆盖源文件
[20:54:49 root@demo-c8 ~]#mkdir /data/test
[20:54:53 root@demo-c8 ~]#cd /data/test
[20:54:56 root@demo-c8 /data/test]#touch `date +%F_%T`.log
[20:55:06 root@demo-c8 /data/test]#ls
2022-08-18_20:55:06.log
- ">": 重定向也可以创建新文件, 如果文件不存在,则创建空文件, 但是如果文件存在, 则清空源文件, 有风险. 如果想清空文件可以用
>


2.9 复制文件和目录
利用cp
命令可以实现文件或目录的复制, 复制文件或目录时, 源文件会保留, 生成新的文件或目录
工作中, 无论是复制还是移动文件, 最好先把源文件和目标文件都备份一下, 避免误操作
root
账号下的cp
命令是cp -i
的别名, 如果目标存在, 会提示是否覆盖, 而普通账号下的cp
不是别名, 复制不会提示是否覆盖
格式:
CP [OPTION]... [-T] SOURCE DEST
CP [OPTION]... SOURCE... DIRECTORY
CP [OPTION]... -t DIRECTORY SOURCE...
复制的不同情况:

1. 源是单个文件:
目标文件不存在: 在目标目录创建目标文件, 将源文件中的内容, 覆盖到新创建的目标文件
cp -a /etc/fstab /tmp/fstab.bak # 将fstab复制到/tmp下, 文件名为fstab.bak
目标存在, 且为文件: 将源文件中的内容, 覆盖到已存在的目标文件. root账号会提示是否覆盖, 普通账号建议用cp -i, 避免数据丢失
cp -a /etc/bashrc /tmp/fstab.bak # 因为fstab.bak已经存在, 所以在cp时会提示是否覆盖
目标存在, 且为目录: 在目标目录下, 创建与源文件同名的文件, 将源文件的内容, 填充至新创建的文件
cp -a /etc/fstab /tmp # 将fstab复制到/tmp下, 文件名为fstab
2. 源是多个文件:
目标不存在: 提示错误
目标存在且为文件: 提示错误, 源是多个文件, 那么目标必须是一个已存在的目录
目标存在且为目录: 在目标目录下, 创建与源文件同名的多个文件, 并且将源文件内容对应写到新创建的目标文件
3. 源是目录, 则必须使用-r/-R递归复制
目标目录不存在: 在目标目录的父目录创建目标目录, 把源目录中的内容, 复制到新创建的目标目录下
cp -a /etc /etc_backup # 在/根目录创建名为etc_backup的目录, 把/etc目录内的内容, 复制到/etc_backup目录下
目标存在且为文件: 提示错误
目标存在且为目录: 在已存在的目录下, 创建与源目录同名的目录, 并且将源目录中的内容, 复制到目标目录下
cp -a /etc /etc_backup # 如果/etc_backup已经存在, 则在/etc_backup目录下, 创建新的目录etc, 把/etc内的内容复制过去
-u: 仅复制源比目标更新newer的文件, 或目标不存在的文件, 两个目录保存了名字相同的文件, 可以用-u去更新, 避免由于要覆盖的文件量太大, 命令执行时间慢
-b: 会把已经存在的目标文件, 在复制前做备份, 因为目标文件存在, cp命令会用源文件覆盖目标文件. 默认只备份一次, 加--backup=numbered
选项会保留多个目标文件版本


[21:01:43 root@demo-c8 /data/test]#ll /dev/zero
crw-rw-rw-. 1 root root 1, 5 Aug 18 13:50 /dev/zero
[21:04:18 root@demo-c8 /data/test]#ll /dev/sda*
# 8 major类型, 0-5minor类型
brw-rw----. 1 root disk 8, 0 Aug 18 13:50 /dev/sda
brw-rw----. 1 root disk 8, 1 Aug 18 13:50 /dev/sda1
brw-rw----. 1 root disk 8, 2 Aug 18 13:50 /dev/sda2
brw-rw----. 1 root disk 8, 3 Aug 18 13:50 /dev/sda3
brw-rw----. 1 root disk 8, 4 Aug 18 13:50 /dev/sda4
brw-rw----. 1 root disk 8, 5 Aug 18 13:50 /dev/sda5
-
cp
过程中, 文件的元数据默认不会全部保留, 可能会丢失一部分
普通文本文件, 三个时间戳可能会变成复制命令执行时的时间, 新产生的文件的所有者也可能发生变化
字符文件, cp /dev/zero /tmp/zero.bak 新的bak文件会变成普通文件
cp对于普通文件影响较小, 不会影响文件类型, 对于特殊文件如字符文件, 复制后, 文件类型会发生变化
- 正常复制时, 无论改不改名, 文件时间都会改变
[12:12:13 root@centos8-2 /data/prac]#cp /etc/issue .
[12:18:07 root@centos8-2 /data/prac]#ll /etc/issue ./issue
-rw-r--r--. 1 root root 23 Jun 3 11:02 /etc/issue
-rw-r--r-- 1 root root 23 Aug 18 12:18 ./issue # mtime发生变化
复制出来的文件, 三个时间戳都是执行复制命令时的时间
[12:18:28 root@centos8-2 /data/prac]#stat !*
stat /etc/issue ./issue
File: /etc/issue
Size: 23 Blocks: 8 IO Block: 4096 regular file
Device: 802h/2050d Inode: 134329398 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-08-17 20:09:23.017015589 +1000
Modify: 2020-06-03 11:02:49.000000000 +1000
Change: 2020-08-12 13:59:15.012002240 +1000
Birth: -
File: ./issue
Size: 23 Blocks: 8 IO Block: 4096 regular file
Device: 805h/2053d Inode: 67108993 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2020-08-18 12:18:04.055021409 +1000 # 复制出来的文件, 三个时间戳都发生了变化, 时间戳都是执行复制命令时的时间
Modify: 2020-08-18 12:18:04.055021409 +1000
Change: 2020-08-18 12:18:04.055021409 +1000
Birth: -
- cp复制特殊文件时,比如字符文件, 文件大小,文件类型,文件时间都会改变
[12:19:37 root@centos8-2 /data/prac]#cp /dev/zero ./zero.bak
^C
[12:22:23 root@centos8-2 /data/prac]#ll !*
ll /dev/zero ./zero.bak
crw-rw-rw- 1 root root 1, 5 Aug 18 10:38 /dev/zero # 1-设备大类别 5-第五个设备
-rw-r--r-- 1 root root 91480064 Aug 18 12:22 ./zero.bak # 字符文件c变成了普通文件, 文件大小和三个时间戳也都变成了复制命令执行的时间
- 文件属主, 属组变化, 用cp命令, 复制不是以当前账号为属主或属组的文件时, 复制出来的文件, 属主和属组会变成当前用户
[12:26:32 root@centos8-2 ~]#cp ~david/test.123 ./hh.txt
[12:26:56 root@centos8-2 ~]#ll !*
ll ~david/test.123 ./hh.txt
-rw-r--r-- 1 root root 6 Aug 18 12:26 ./hh.txt # 复制出来的文件的属主和属组变成了root, 源文件是david
-rw-rw-r-- 1 david david 6 Aug 18 12:26 /home/david/test.123
- 如何复制时保留源文件属性
-p 选项: 复制时, 保留源文件部分属性, 包括权限rwx, owner, group, 和atime和mtime. ctime是文件元数据修改的时间, 和源文件是不一样的
-p same as --preserve=mode,ownership,timestamps
--preserve[=ATTR_LIST] preserve the specified attributes (default:
mode,ownership,timestamps), if possible
additional attributes: context, links, xattr,
all
[12:29:16 root@centos8-2 ~]#cp -p ~david/test.123 ./lala.txt
[12:29:39 root@centos8-2 ~]#ll !*
ll -p ~david/test.123 ./lala.txt
-rw-rw-r-- 1 david david 6 Aug 18 12:26 /home/david/test.123
-rw-rw-r-- 1 david david 6 Aug 18 12:26 ./lala.txt
但是-p
也有可能丢失文件类型信息, 比如, 复制软链接时会丢失软链接属性, 而实际复制的是软链接指向的文件, 结果就是源文件是软链接, 复制出来的文件变成了普通文件
# /etc/grub2.cfg是/boot/grub2/grub.cfg的软链接, 如果不加任何选项进行复制, 那么复制后的目标文件会变成普通文件
[12:36:39 root@centos8-2 /data/prac]#cp /etc/grub2.cfg ./grub2.bak
[12:36:49 root@centos8-2 /data/prac]#ll !*
ll /etc/grub2.cfg ./grub2.bak
lrwxrwxrwx. 1 root root 22 Apr 15 00:53 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
-rw-r--r-- 1 root root 5140 Aug 18 12:36 ./grub2.bak
-d 选项: 复制快捷方式本身, 不会复制软链接指向的实际文件, 这样可以保留软链接的属性

-r/-R 选项: 递归复制整个目录内容, 不加-r/-R是无法复制整个目录的
[21:04:22 root@demo-c8 /data/test]#cp /boot .
cp: -r not specified; omitting directory '/boot'
[21:10:39 root@demo-c8 /data/test]#cp -r /boot .
-a 选项: 用于归档, 相当于-dR, --preserv=all, 保留所有属性,常用于备份功能
[13:15:08 root@centos8-2 /data/prac]#cp -a /etc/grub2.cfg ./grub2.cfg.bak
[13:16:25 root@centos8-2 /data/prac]#ll !*
ll -a /etc/grub2.cfg ./grub2.cfg.bak
lrwxrwxrwx. 1 root root 22 Apr 15 00:53 /etc/grub2.cfg -> ../boot/grub2/grub.cfg
lrwxrwxrwx. 1 root root 22 Apr 15 00:53 ./grub2.cfg.bak -> ../boot/grub2/grub.cfg
# 典型备份命令, 利用花括号扩展, 这样备份文件名字会在源文件后加.bak
cp -a test.sh{,.bak}
cp命令多次执行时效果不一样
[13:17:11 root@centos8-2 /data/prac]#ls
[13:25:19 root@centos8-2 /data/prac]#cp /boot /data/prac/bootbak #复制目录时, 需要-r或者-R递归复制目录里的文件
cp: -r not specified; omitting directory '/boot'
[13:25:36 root@centos8-2 /data/prac]#cp -r /boot /data/prac/bootbak #第一次复制时, 由于目标目录不存在, 则cp会把源目录,复制到指定目录下,并且更名为指定的新目录
[13:25:40 root@centos8-2 /data/prac]#ls
bootbak
[13:25:44 root@centos8-2 /data/prac]#cp -r /boot /data/prac/bootbak #第二次复制,由于目标目录已经存在, 则cp命令会把源目录复制到该已经存的目录下作为子目录, 这时bootbak目录里包含第一次从boot里复制来的文件,和第二次复制来的整个boot目录,
[13:25:48 root@centos8-2 /data/prac]#ls
bootbak
[13:25:50 root@centos8-2 /data/prac]#ls bootbak/
boot
config-4.18.0-193.el8.x86_64
efi
grub2
initramfs-0-rescue-f072eccf217a4374b5b44dc4461b3c54.img
initramfs-4.18.0-193.el8.x86_64.img
initramfs-4.18.0-193.el8.x86_64kdump.img
loader
lost+found
System.map-4.18.0-193.el8.x86_64
vmlinuz-0-rescue-f072eccf217a4374b5b44dc4461b3c54
vmlinuz-4.18.0-193.el8.x86_64
[13:26:00 root@centos8-2 /data/prac]#cp -r /boot /data/prac/bootbak #第三次再复制, 由于bootbak目录存在, 则cp把boot复制到bootbak目录下,但是由于boot目录在第二次已经复制时已经复制到boobak目录了,那么就会提示是否替换内容
#如果不想提示覆盖, 可以用\cp, cp的-f并不是强制覆盖
cp: overwrite '/data/prac/bootbak/boot/vmlinuz-4.18.0-193.el8.x86_64'?
cp: overwrite '/data/prac/bootbak/boot/System.map-4.18.0-193.el8.x86_64'?
cp: overwrite '/data/prac/bootbak/boot/config-4.18.0-193.el8.x86_64'?
- v选项: 显示复制的详细过程
修改配置文件之前要备份
/etc/motd: 用户登录后提示信息设置, 全局用户有效
cp -av /etc/motd{,.bak}
2.10 移动文件和重命名文件
mv命令可以实现文件或目录的移动和改名
同一个分区, 移动数据, 速度很快, 因为数据位置没有变化
不同分区, 移动数据, 速度相对慢, 因为数据位置发生了变化
mv支持:
- 单个文件移动到一个目录
- 多个文件移动到一个目录
- 不支持多个文件,分别移动到不同目录
格式:
mv [OPTION]... [-T] SOURCE DEST
mv [OPTION]... SOURCE... DIRECTORY
nv [OPTION]... -t DIRECTORY SOURCE...
常用选项:
-i 交互式, 提示目标存在是否覆盖, root用户使用mv会提示是否覆盖
-f 强制移动
-b 当目标文件/目录已存在, 在覆盖前先备份
mv只能一次处理一个文件的改名操作, 而rename可以批量修改文件名
格式:
rename [options] <expression> <replacement> <file>...
rename的expression就是目标文件的文件名中需要修改的部分, replacement为替换后的内容. <file>支持通配符
rename适用于当多个文件的文件名有相同的部分时, 需要把这个相同的部分统一做修改
范例1: 为所有的conf文件, 加上后缀.bak
rename "conf" "conf.bak" file*
范例2: 去掉所有文件的.bak后缀
rename ".bak" "" *.bak
- 范例3: 为指定的文件名加后缀
[root@demo-c8 ~]# cd /tmp
[root@demo-c8 tmp]# rm -rf *
[root@demo-c8 tmp]# touch f{1..10}.txt
[root@demo-c8 tmp]# ll
total 0
-rw-r--r--. 1 root root 0 Aug 18 23:33 f10.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f1.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f2.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f3.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f4.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f5.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f6.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f7.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f8.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f9.txt
[root@demo-c8 tmp]# rename "txt" "txt.bak" f[1-9].txt
[root@demo-c8 tmp]# ll
total 0
-rw-r--r--. 1 root root 0 Aug 18 23:33 f10.txt
-rw-r--r--. 1 root root 0 Aug 18 23:33 f1.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f2.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f3.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f4.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f5.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f6.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f7.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f8.txt.bak
-rw-r--r--. 1 root root 0 Aug 18 23:33 f9.txt.bak
2.11 删除文件
rm命令可以删除Linux系统上的文件, 此命令非常危险
格式:
rm [OPTION]... FILE
常用选项:
-i 交互式
-f 强制删除
-r 递归
--no-preserve-root 删除/目录
root上的rm是别名, 会提示是否删除, rm -f会强制删除文件, rm -rf会强制删除目录
Linux中, 即使文件正在编辑,也是可以删除的
移动, 复制, 删除大文件时, 要找系统空闲的时间进行, 避免对系统造成过大压力
范例1: 删根
rm -rf / # 从CentOS6开始, 已经禁止执行此命令
[root@demo-c8 tmp]# rm -rf /
rm: it is dangerous to operate recursively on '/'
rm: use --no-preserve-root to override this failsafe
rm -rf /* 仍然可以执行, 工作中要命令禁止此命令
比如: 如果要删除test目录内的内容, 保留test目录, 需要执行rm -rf test/*
[root@demo-c8 data]# ls
test
[root@demo-c8 data]# rm -rf test/*
但是, 一旦在test和/*中间, 无意间加了空格, 那么就是执行rm -rf test和rm -rf /*, 就会把根删了
范例2: 将rm设置为mv的别名
# root用户, rm为rm -i的别名, 删除会提示是否删除
# 把rm设置为mv的别名后, 执行rm是无法删除任何文件和目录的, 因为mv命令需要两个参数, 此时, 执行rm的用户就会知道rm是不让使用的, 需要执行mv命令
[root@demo-c8 data]# alias rm
alias rm='rm -i'
[root@demo-c8 data]# alias rm=mv
[root@demo-c8 data]# alias rm
alias rm='mv'
[root@demo-c8 data]# rm test
mv: missing destination file operand after 'test'
Try 'mv --help' for more information.
[root@demo-c8 data]# rm -rf test
mv: invalid option -- 'r'
Try 'mv --help' for more information.
# 或者把rm改成echo, 执行rm提示不要删除文件也行
[root@demo-c8 data]# alias rm="echo DO NOT RM ANY FILES"
[root@demo-c8 data]# rm test
DO NOT RM ANY FILES test
- 特殊格式文件的创建和删除
# 创建/data/-f文件
[root@demo-c8 data]# touch ./-f
[root@demo-c8 data]# ll
total 0
-rw-r--r--. 1 root root 0 Aug 18 23:52 -f
[root@demo-c8 data]# ls
-f
# 删除/data/-f文件
方法1. 使用相对或者绝对路径
方法2. 使用rm -- -f
[root@demo-c8 data]# rm -- -f
[root@demo-c8 data]# ll
total 0
Linux的rm删除文件, 只是删除文件名, 磁盘文件还是存在的, 如果想把磁盘文件也删了, rm不能彻底删除干净, 可以使用一些工具进行恢复
- shred会将文件彻底清除, 在安全要求较高的情况下, 可以使用shred安全删除文件
格式:
shred [OPTION]... FILE...
选项:
-z 最后一次覆盖文件时添加0, 以隐藏覆盖操作
-v 显示操作进度
-u 覆盖后截断并删除文件
-n 数字 指定覆盖文件内容的次数, 默认是3次
范例: shred删除文件
[root@demo-c8 data]# cp -a /etc/passwd ./passwd.txt
[root@demo-c8 data]# ll
total 4
-rw-r--r--. 1 root root 2658 Aug 15 17:05 passwd.txt
[root@demo-c8 data]# shred -zvun 5 passwd.txt
shred: passwd.txt: pass 1/6 (random)...
shred: passwd.txt: pass 2/6 (ffffff)...
shred: passwd.txt: pass 3/6 (random)...
shred: passwd.txt: pass 4/6 (000000)...
shred: passwd.txt: pass 5/6 (random)...
shred: passwd.txt: pass 6/6 (000000)...
shred: passwd.txt: removing
shred: passwd.txt: renamed to 0000000000
shred: 0000000000: renamed to 000000000
shred: 000000000: renamed to 00000000
shred: 00000000: renamed to 0000000
shred: 0000000: renamed to 000000
shred: 000000: renamed to 00000
shred: 00000: renamed to 0000
shred: 0000: renamed to 000
shred: 000: renamed to 00
shred: 00: renamed to 0
shred: passwd.txt: removed
2.12 目录操作
2.12.1 tree
常见选项:
默认显示目录内所有层级的目录和文件
-d 只列出所有层级的目录结构, 不显示文件
-L # 指定显示的层级数目
-P pattern 只显示由指定通配符匹配到的路径
[root@demo-c8 ~]# tree -d -L 2 /tmp
/tmp
├── systemd-private-a07e43724f874410b0748e3d9d005f9d-bluetooth.service-phNwvu
│?? └── tmp
├── systemd-private-a07e43724f874410b0748e3d9d005f9d-colord.service-8Pl0aZ
│?? └── tmp
├── systemd-private-a07e43724f874410b0748e3d9d005f9d-ModemManager.service-mSF6wP
│?? └── tmp
├── systemd-private-a07e43724f874410b0748e3d9d005f9d-rtkit-daemon.service-RhHyFa
│?? └── tmp
└── vmware-root_953-3979774151
2.12.2 创建目录 mkdir
常见选项:
-p 自动递归创建所需的各层级目录
-v 显示详细信息
-m MODE 创建目录时直接指定权限
2.12.3 删除空目录rmdir
常见选项:
-p 递归删除父级空目录, rmdir -p 逆向删除空目录, 父目录如果是空的, 也会被删除
-v 显示详细信息
rmdir只能删除空目录, 如果目录内有内容,则无法删除, 因此rmdir可以用来批量删除空目录
如果想要删除非空目录, 需要用rm -rf, 递归删除目录树
- 磁盘挂载点目录内容可以清空, 但是无法把目录也删除了
[root@demo-c8 ~]# rm -rf /data
rm: cannot remove '/data': Device or resource busy
[root@demo-c8 ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 100G 0 disk
├─sda1 8:1 0 1G 0 part /boot
├─sda2 8:2 0 40G 0 part /
├─sda3 8:3 0 2G 0 part [SWAP]
├─sda4 8:4 0 1K 0 part
└─sda5 8:5 0 40G 0 part /data # sda5挂载到了/data下, 所以/data是无法被删除的, 只能删除/data里的内容
sr0 11:0 1 7.7G 0 rom
- 给rm设置别名, 执行删除时, 会把要删除的文件, 移动到指定目录
# 仅对rm命令有效, 如果执行rm -rf是会报错的, 因为mv命令没有-rf选项, 所以rm -rf也是无法执行的
alias rm='DIR=/data/backup_`date +%F_%T`; mkdir -p $DIR; mv -t $DIR'
- 练习
如何创建目录/testdir/dir1/x, /testdir/dir1/y, /testdir/dir1/x/a,/testdir/dir1/x/b,/testdir/dir1/y/a,/testdir/dir1/y/b
mkdir -p /testdir/dir1/{x,y}/{a,b}
[root@demo-c8 ~]# mkdir -p /testdir/dir1/{x,y}/{a,b}
[root@demo-c8 ~]# tree /testdir/dir1
/testdir/dir1
├── x
│ ├── a
│ └── b
└── y
├── a
└── b
如何创建目录/testdir/dir2/x. /testdir/dir2/y, /testdir/dir2/x/a, /testdir/dir2/x/b
mkdir -p /testdir/dir2/x/{a,b}
mkdir -p /testdir/dir2/y
[root@demo-c8 ~]# mkdir -p /testdir/dir2/{x/{a,b},y}
[root@demo-c8 ~]# tree -d /testdir/dir2
/testdir/dir2
├── x
│ ├── a
│ └── b
└── y
如何创建目录/testdir/dir3, /testdir/dir4, /testdir/dir5, /testdir/dir5/dir6, /testdir/dir5/dir7
mkdir -p /testdir/dir{3,4,5/dir{6,7}}
[root@demo-c8 ~]# mkdir -p /testdir/dir{3,4,5/dir{6,7}}
[root@demo-c8 ~]# tree -d /testdir/
/testdir/
├── dir1
│ ├── x
│ │ ├── a
│ │ └── b
│ └── y
│ ├── a
│ └── b
├── dir2
│ ├── x
│ │ ├── a
│ │ └── b
│ └── y
├── dir3
├── dir4
└── dir5
├── dir6
└── dir7
3 文件元数据和节点表结构
3.1 inode表结构
3.1.1 文件
每个文件的属性信息, 比如: 文件的大小, 时间, 类型等, 成为文件的元数据(meta data). 这些元数据是存放在node(index node)表中. node表中有很多条记录组成, 每一条记录对应的存放了一个文件的元数据信息. 而文件名称并不属于一个文件的元数据, 文件名称属于目录的数据部分
- 元数据
每一个node表记录对应的保存了以下信息, 也就是文件的元数据信息:
inode number 节点号
文件类型
权限
UID
GID
链接数(指向这个文件名的名称个数, 硬链接个数)
该文件的大小和不同的时间戳
指向磁盘上文件的数据块指针
有关文件的其他数据
inode 文件节点编号, 整个系统唯一, 用来与其他文件区分开来, 操作系统会自动分配,底层管理文件会使用inode
# -i 选项会显示inode编号
[root@demo-c8 ~]# ls -i
103221107 anaconda-ks.cfg 666292 Downloads 666294 Music 68360890 Public 68360891 Videos
101475423 Documents 101475459 initial-setup-ks.cfg 35080439 Pictures 35080438 Templates
- 元数据和实际数据存放
一个硬盘, 可以大致分为两个空间, 一个存放元数据, 另一部分空间存放时间数据.
元数据: 文件属性, 索引, 通过指针指向文件在磁盘真正存放文件的位置
实际数据: 通过指针, 指向文件在磁盘上真正存放的位置
# 1385为文件实际数据大小
[root@demo-c8 ~]# ll -i anaconda-ks.cfg
103221107 -rw-------. 1 root root 1385 Aug 15 17:06 anaconda-ks.cfg
block块: 文件在磁盘存放的最小单元, 一般4kb, 即使一个文件大小不到4kb, 也要占4kb
- 指针
指针: 通过指针, 指向文件在磁盘上真正存放的位置
-
直接指针: 直接指向数据在磁盘的存放位置. 直接指针有12个, 每个指针指向一个4k块, 那么最多有12个指针, 指向12个4k文件, 也就是最多有48k的数据可以通过直接指针找到
-
间接指针: 如果文件大于48k, 比如1M, 那么要使用间接指针
间接指针指向一个索引块, 该索引块并不是数据块, 其大小为4kb, 该索引快被切割成多个相同大小的小块, 每块占4byte, 所以总共有1024个小块. (这个索引块一共4k, 每块占4b, 1kb=1024b, 所以一共有1024个小块)
每个小块就是一个指针, 指向4k大小的空间, 一共1024个指针, 指向总大小为4k*1024=4096kb大小的数据, 也就是指向4M数据, 所以通过间接指针, 最多可以找到4M大小的数据
-
双重间接指针: 最多指向4G大小, 双重指针包含两个索引块
-
三重间接指向: 最多指向4T大小, 三重指针包含三个索引快
-
总结:
单个文件数据小于4k, 用直接指针
单个文件数据大于4k小于4M,用间接指针
单个文件数据大于4M, 双重,三重间接指针
数据文件越大, 找到该文件花费时间越多
- 不同的文件系统, 工作原理不同, 以上指针工作过程是针对ext4系统, 早期LinuxFAT系统, 最多支持4M. 如今ext4和xfx支持更大的单个文件
3.1.2 目录
- 目录
目录是个特殊文件, 一般文件存放的东西就是其数据本身, 而目录文件的数据内容保存了此目录中, 文件的列表(文件名), 和文件inode number的对应关系
- 目录也会占用一个inode编号
文件引用的是inode编号
人是通过文件名来引用一个文件
一个目录的实际数据是目录下的文件名和文件名对应inode号之间的映射
文件名是是目录里的内容, 属于目录的数据部分
[root@demo-c8 ~]# ls -i
103221107 anaconda-ks.cfg 666292 Downloads 666294 Music 68360890 Public 68360891 Videos
101475423 Documents 101475459 initial-setup-ks.cfg 35080439 Pictures 35080438 Templates
-
inode节点表并不保存文件名信息, 也就是说文件元数据不包含文件名, 而是包含, mode权限, owner信息,大小,时间戳,各种时间戳
-
目录则存放的是里面的文件列表(文件名)和inode节点编号对应信息

3.1.3 inode空间管理
-
一般inode表会占用文件系统磁盘空间的1%. 节点编号不是无限的, 每个分区会有固定的节点编号数量, 用光了就无法创建新的文件和目录了. 分区空间越大, 节点编号越多
-
df -i
查看磁盘inode使用情况
[root@demo-c8 ~]# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
devtmpfs 989313 443 988870 1% /dev
tmpfs 996355 1 996354 1% /dev/shm
tmpfs 996355 907 995448 1% /run
tmpfs 996355 17 996338 1% /sys/fs/cgroup
/dev/sda2 20971520 119379 20852141 1% /
/dev/sda5 20971520 20 20971500 1% /data
/dev/sda1 65536 309 65227 1% /boot
tmpfs 996355 20 996335 1% /run/user/42
tmpfs 996355 11 996344 1% /run/user/0
-
当inode不足时, 可以把小文件删了, 释放inode. 如果在一个分区里, 建的都是小文件, 那么就会出现空间大小有剩余, 但是inode不足, 导致无法创建新的文件或目录
-
访问一个文件的过程: 先在目录里看文件的节点编号, 然后查看节点表, 节点表有节点编号和指针对应关系, 通过节点编号, 找到指针,也就找到了文件在磁盘中的存储位置
3.1.4 inode管理案例
- 案例1: 磁盘分区显示 No space left on device
No space left on device一般是两种原因, 1. inode用光, 2. 磁盘大小用光
通过`df -i`确定inode是否用光, 还是磁盘空间用光了
- 案例2: rm删除文件后,磁盘空间没有立即释放, 通过
df
命令还是显示磁盘空间快满了
有可能是被删除的文件,正在被访问, 例如应用程序文件, 这时需要找到该文件, 把对应的应用程序关闭, 然后再删除该文件
构建实验环境:
# 先确定磁盘分区使用情况
[root@demo-c8 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.8G 0 3.8G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 9.7M 3.8G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda2 40G 4.3G 36G 11% /
/dev/sda5 40G 318M 40G 1% /data
/dev/sda1 976M 189M 721M 21% /boot # boot分区还剩721M空间
tmpfs 779M 1.2M 778M 1% /run/user/42
tmpfs 779M 4.0K 779M 1% /run/user/0
# 利用/dev/zero文件, 在/boot分区创建一个测试大文件
[root@demo-c8 ~]# cp /dev/zero /boot/zero.bak
cp: error writing '/boot/zero.bak': No space left on device
[root@demo-c8 ~]# ll -h /boot
-rw-r--r--. 1 root root 772M Aug 19 15:06 zero.bak
[root@demo-c8 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.8G 0 3.8G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 9.7M 3.8G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda2 40G 4.3G 36G 11% /
/dev/sda5 40G 318M 40G 1% /data
/dev/sda1 976M 960M 0 100% /boot # boot分区被撑满
tmpfs 779M 1.2M 778M 1% /run/user/42
tmpfs 779M 4.0K 779M 1% /run/user/0
# 在另一个窗口, 通过vim打开/boot/zero.bak文件, 模拟文件再被应用程序占用
[root@demo-c8 ~]# vim /boot/zero.bak
# 回到之前的窗口, 通过rm -rf把zero.bak删除
[root@demo-c8 ~]# rm -rf /boot/zero.bak
# 可以看到, 即使zero.bak被删除了, boot分区空间还是没有释放
[root@demo-c8 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.8G 0 3.8G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 9.7M 3.8G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda2 40G 4.3G 36G 11% /
/dev/sda5 40G 318M 40G 1% /data
/dev/sda1 976M 960M 0 100% /boot
tmpfs 779M 1.2M 778M 1% /run/user/42
tmpfs 779M 4.0K 779M 1% /run/user/0
[root@demo-c8 ~]# ll /boot
total 184388
-rw-r--r--. 1 root root 187648 May 8 2020 config-4.18.0-193.el8.x86_64
drwxr-xr-x. 3 root root 4096 Aug 15 16:53 efi
drwx------. 4 root root 4096 Aug 15 17:05 grub2
-rw-------. 1 root root 98497217 Aug 15 17:03 initramfs-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4.img
-rw-------. 1 root root 50808400 Aug 15 17:06 initramfs-4.18.0-193.el8.x86_64.img
-rw-------. 1 root root 17536677 Aug 15 17:16 initramfs-4.18.0-193.el8.x86_64kdump.img
drwxr-xr-x. 3 root root 4096 Aug 15 17:00 loader
drwx------. 2 root root 16384 Aug 15 16:52 lost+found
-rw-------. 1 root root 3909996 May 8 2020 System.map-4.18.0-193.el8.x86_64
-rwxr-xr-x. 1 root root 8913656 Aug 15 17:02 vmlinuz-0-rescue-eaab49a3b5f746ae847c443ec9bc62c4
-rwxr-xr-x. 1 root root 8913656 May 8 2020 vmlinuz-4.18.0-193.el8.x86_64
步骤1: 通过lsof 命令查询刚被删除的文件是被哪个程序所使用的, 找到进程的pid
# lsof命令会显示正在被打开的文件信息
# lsof | grep deleted, 定位到被删除的那个文件, 找到进程编号
[root@demo-c8 ~]# lsof | grep deleted
#程序名称 #进程编号
vim 4238 root 5r REG 8,1 809349120 308 /boot/zero.bak (deleted)
步骤2: 找到该程序, 关闭进程或者如果是前台运行的程序, 就把运行窗口关了, 然后在df
查看, 磁盘空间就会释放了
# 关闭vim程序
[root@demo-c8 ~]# kill -9 4238
boot/zero.bak" Killed
# 再次通过df查看, boot分区空间已经被释放
[root@demo-c8 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 3.8G 0 3.8G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 9.7M 3.8G 1% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda2 40G 4.3G 36G 11% /
/dev/sda5 40G 318M 40G 1% /data
/dev/sda1 976M 189M 721M 21% /boot
tmpfs 779M 1.2M 778M 1% /run/user/42
tmpfs 779M 4.0K 779M 1% /run/user/0
- 如果程序不允许关闭呢?, 很多应用程序需要实时被用户访问, 不能被随意关闭, 此时就要先把文件清空, 释放空间, 然后再把文件删了
方法1: 通过重定向> 要删除的文件名
, 把文件清空, 但是只有bash shell支持重定向, 此方法不通用
- 切换系统使用的shell, 需要下载对应包, 执行对应的命令, 比如执行csh, 就会进行cshell
方法2: 通用方法, cat是外部命令, 不依赖于shell类型
cat /dev/null > 文件名, 这样就可以清空大文件, 大小为0了 空间就释放了
因此正确删除正在运行的大文件, 立即释放空间的方法
cat /dev/null > FILE, 先把文件清空
rm -rf FILE, 再把文件删除
3.2 硬链接 hard link
硬链接本质是给文件起一个新的名称, 实质上硬链接和原始文件都是同一个文件, 没有区别
硬链接特性:
- 创建硬链接会在对应的目录中增加额外的记录项, 以引用文件
- 同一个文件不同名字, 本质是同一个文件
- 通过哪个文件访问,看到的内容都是同一个文件
- 修改任意一个文件,在其他文件也生效
- 两个不同名字的文件, 共用相同的节点编号,指向磁盘相同的空间
- 如果节点编号一样, 那就是同一个文件
- 硬链接和复制不一样, 复制是生成一个完全不同的新文件, 节点编号不同, 硬链接是给一个文件创建不同的名字, 实际都是同一个文件
-
ll
命令显示的权限和owner中间的数字就是这个文件的硬链接数量, 也就是同一个文件有几个名字 - 硬链接和源文件都是对应于同一个文件系统上的同一个物理文件, 修改任意一个硬链接, 都会影响其他硬链接
- 硬链接和源文件的inode相同
- 创建硬链接时, 文件的链接数加一, 删除时减一
- 不能跨越驱动器或磁盘分区创建硬链接
- 不支持对目录创建硬链接
- 删除文件时: rm命令会递减文件的链接数, 一个文件如果是存在的, 那么最少有一个链接数, 当链接数为0时, 文件就被删除了
格式:
ln 源文件 硬链接文件
[15:07:52 root@centos8-2 /data/prac]#touch f1.txt
[15:07:57 root@centos8-2 /data/prac]#ln f1.txt f2.txt # ln src dest
[15:08:04 root@centos8-2 /data/prac]#ls
f2.txt f1.txt
[15:08:05 root@centos8-2 /data/prac]#ll
total 0
-rw-r--r-- 2 root root 0 Aug 18 15:07 f2.txt
-rw-r--r-- 2 root root 0 Aug 18 15:07 f1.txt
[15:08:19 root@centos8-2 /data/prac]#ll -i
total 0
67109585 -rw-r--r-- 2 root root 0 Aug 18 15:07 f2.txt
67109585 -rw-r--r-- 2 root root 0 Aug 18 15:07 f1.txt
当f1.txt 是 f2.txt的硬链接时, 实际两个文件是同一个文件的不同名字, 两个文件具有相同的元数据, 也就是inode编号,等其他信息,见上, 元数据相同, 指针相同, 指向磁盘中相同的数据空间

为什么rm无法彻底删除磁盘文件?
如果一个文件有多个硬链接, 那么删除硬链接, 仅仅是删除一个文件名, 哪怕把最后一个文件名也给删了, 也是删除的元数据, 节点编号被收回
但是数据还是保存在磁盘相应的位置, 因此只要给对应的磁盘空间分配节点编号,创建元数据和指针就可以重新访问数据了, 这就是rm删除文件后, 可以恢复的逻辑
硬链接缺点:
- 不能跨分区创建硬链接
每个分区都有自己独立的节点数量和编号, 每个分区自己分配自己的节点编号和数量, 不能跨节点创建硬链接,
因为硬链接是给一个文件创建不同的名称, 实际两个名字指向相同的磁盘空间, 相同的磁盘空间必定在同一个分区, 跨分区创建因此不可能, 违反了硬链接本质 - 不支持文件夹硬链接, 容易产生目录嵌套, 造成死循环
- 因此用的不多
# root在/分区, data在/data分区, 无法跨分区创建硬链接
[15:38:12 root@centos8-2 /data/prac]#ln /root/.bashrc /data/test.txt
ln: failed to create hard link '/data/test.txt' => '/root/.bashrc': Invalid cross-device link
3.3 软链接 soft link
软链接
- 创建出来的软链接必须是不存在的
- 也就是不能把一个已经存在的文件, 创建成另一个文件的软链接
- 软链接和原始文件并不是同一个文件, 节点编号不同
- 软链接实际就是快捷方式, 修改原始文件后, 通过快捷方式访问也能看到变化
- 软链接可以跨分区, 硬链接无法跨分区, 因为硬链接的inode节点编号相同, 节点编号相同的文件必是在同一个分区, 指向同一个文件
- 软链接对应原始文件如果被删除, 即使软链接虽然还在, 但是也无法使用
- 原始文件删除后, 可以把软链接再指向新的文件, 这样可以访问新的内容
- 可以给软链接也创建软链接
- 软链接指向的是另一个文件的存放路径, 软链接文件的大小为其指向的路径字符串的长度, 创建或删除软链接不会增加或者减少目标文件inode的链接数
- 软链接如果使用相对路径, 是相对于源文件的路径, 而非相对于当前目录
格式:
ln -s src dest
ln -s appv1.0 app
ln -s app testapp
[root@demo-c8 data]# mkdir appv1.0
[root@demo-c8 data]# ln -s appv1.0/ app

# 通过软链接和原始目录, 都可以访问到源目录中的文件
[root@demo-c8 data]# touch appv1.0/file{1..9}.log
[root@demo-c8 data]# ll app/
total 0
-rw-r--r--. 1 root root 0 Aug 19 18:46 file1.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file2.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file3.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file4.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file5.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file6.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file7.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file8.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file9.log
[root@demo-c8 data]# ll appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 18:46 file1.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file2.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file3.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file4.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file5.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file6.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file7.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file8.log
-rw-r--r--. 1 root root 0 Aug 19 18:46 file9.log
- 软链接可以跨分区创建
# boot_data在/data分区, 而/boot是单独的分区
[root@demo-c8 data]# ll
total 0
lrwxrwxrwx. 1 root root 5 Aug 19 18:55 boot_data -> /boot
- 创建软链接, 链接数是不增长的
[root@demo-c8 data]# touch f1.txt
[root@demo-c8 data]# ll -i f1.txt # ll -i显示inode编号, 不加-i不显示编号, 无论加不加-i都会显示文件的链接数
133 -rw-r--r--. 1 root root 0 Aug 19 18:55 f1.txt # 链接数为1
[root@demo-c8 data]# ln -s f1.txt f11.txt
[root@demo-c8 data]# ll
total 0
lrwxrwxrwx. 1 root root 6 Aug 19 19:01 f11.txt -> f1.txt
-rw-r--r--. 1 root root 0 Aug 19 18:55 f1.txt # 创建软链接f11.txt后, f1链接数还是1
硬链接和原始文件是平等关系, 把原始文件删了, 硬链接还能用
软链接依赖于原始文件, 原始文件删了, 软链接就无法使用了
- 相对路径: 创建软链接时要用绝对路径指定源文件的位置, 否则如果软链接和源文件不在同一个目录下时, 软链接是找不到源文件的, 因为软链接文件默认会在自己所在的目录下去找源文件, 案例如下
[root@demo-c8 data]# ln -s f1.txt /boot/f111.txt
[root@demo-c8 data]# ll /boot/f111.txt
lrwxrwxrwx. 1 root root 6 Aug 19 19:03 /boot/f111.txt -> f1.txt

源文件f1.txt在/data目录下, 软链接文件f111.txt在/boot目录下, 因为创建/boot/f111.txt时, 使用的是相对路径指定的源文件, 所以/boot/f111.txt软链接会认为f1.txt这个源文件和自己一样, 在/boot目录, 但是/boot目录是没有f1.txt文件的, 所以就找不到源文件, 会显示红色, 说明找不到源文件
软链接指向的源文件显示红色可能是因为使用了相对路径, 造成软链接文件找不到原始文件, 也可能是因为原始文件被删除了
- 或者源文件要用目标软链接所在的路径的相对路径来指明源文件所在位置, Linux系统自带的软链接一般都是使用相对路径

一般相对路径都是相对于当前目录, 但是在创建软链接时相对路径是相对于软链接文件的路径
# 相对路径创建软连接案例
[root@demo-c8 data]# touch f1.txt # 创建源文件
[root@demo-c8 data]# ll
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:31 f1.txt
[root@demo-c8 data]# ln -s ../data/f1.txt /boot/f11.txt # 根据目标软链接文件所在的目录, 用相对路径书写源文件所在位置. 目标软链接在/boot目录, /boot上一层是/, 根的下一层是/data, 所以原始文件的路径为 ../data/f1.txt
[root@demo-c8 data]# ll /boot/f11.txt
lrwxrwxrwx. 1 root root 14 Aug 19 19:31 /boot/f11.txt -> ../data/f1.txt
- 软链接应用场景: 软件升级
用户使用访问软链接/app这个目录
但是软链接/app这个目录实际指向的目录是在发生变化的
当开发出1.0版本时, 可以创建软链接, ln -s /appv1.0 /app
当/appv2.0准备上线时, 只需要把/app这个软链接删除, 重新再建一个/app软链接指向/appv2.0即可
rm -rf /app
ln -s /appv2.0 /app
因为不能把已经存在的文件, 创建成别的文件的软链接, 所以要先把/app删除再重新创建
删除软连链接时容易造成的误操作
删除软链接目录时,不要tab补全
当给一个目录创建了软链接后, 如果我们想要把软链接删除, 这时不能给软链接目录自动补全, 否则系统会把软链接名字替换成真正的目录名字, 然后把真正目录里的内容都删了, 结果就是软链接不会被删, 真实的目录本身也不会被删除, 但是目录内的内容都没了
- 问题演示
# 准备原始目录和文件
[root@demo-c8 data]# mkdir appv1.0
[root@demo-c8 data]# touch appv1.0/f{1..9}.txt
[root@demo-c8 data]# ll appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:18 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f9.txt
# 创建软链接
[root@demo-c8 data]# ln -s appv1.0/ app
[root@demo-c8 data]# ll app
lrwxrwxrwx. 1 root root 8 Aug 19 19:19 app -> appv1.0/
[root@demo-c8 data]# ll
total 0
lrwxrwxrwx. 1 root root 8 Aug 19 19:19 app -> appv1.0/
drwxr-xr-x. 2 root root 132 Aug 19 19:18 appv1.0
[root@demo-c8 data]# ll app/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:18 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:18 f9.txt
# 删除app软链接, 并且补全路径分隔符
[root@demo-c8 data]# rm -rf app/ # 补全了分隔符, 会把原始目录内的文件全部删除, 但是软链接目录和原始目录本身会保留
[root@demo-c8 data]# ll
total 0
lrwxrwxrwx. 1 root root 8 Aug 19 19:19 app -> appv1.0/
drwxr-xr-x. 2 root root 6 Aug 19 19:20 appv1.0
[root@demo-c8 data]# ll app/
total 0
[root@demo-c8 data]# ll appv1.0/
total 0
- 正确做法
# 在appv1.0中, 先把删除的文件创建出来
[root@demo-c8 data]# touch appv1.0/f{1..9}.txt
[root@demo-c8 data]# ll app
app/ appv1.0/
[root@demo-c8 data]# ll appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:22 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f9.txt
# 删除时, 不给软链接目录补全路径分隔符
[root@demo-c8 data]# rm -rf app
[root@demo-c8 data]# ll
total 0
drwxr-xr-x. 2 root root 132 Aug 19 19:22 appv1.0 # 这样软链接目录自己会被删除, 而原始目录和文件都会被保存
[root@demo-c8 data]# ll appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:22 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f9.txt
- 复制一个目录时, 如果用 /boot 或者 /boot/ 那么复制的是整个目录, 如果用的是 /boot/* 那么只复制目录内的内容, 目录本身不会被复制
[root@demo-c8 data]# rm -rf /tmp/*
[root@demo-c8 data]# ls /tmp
[root@demo-c8 data]# cp -av appv1.0/ /tmp
'appv1.0/' -> '/tmp/appv1.0'
'appv1.0/f1.txt' -> '/tmp/appv1.0/f1.txt'
'appv1.0/f2.txt' -> '/tmp/appv1.0/f2.txt'
'appv1.0/f3.txt' -> '/tmp/appv1.0/f3.txt'
'appv1.0/f4.txt' -> '/tmp/appv1.0/f4.txt'
'appv1.0/f5.txt' -> '/tmp/appv1.0/f5.txt'
'appv1.0/f6.txt' -> '/tmp/appv1.0/f6.txt'
'appv1.0/f7.txt' -> '/tmp/appv1.0/f7.txt'
'appv1.0/f8.txt' -> '/tmp/appv1.0/f8.txt'
'appv1.0/f9.txt' -> '/tmp/appv1.0/f9.txt'
[root@demo-c8 data]# ll /tmp
total 0
drwxr-xr-x. 2 root root 132 Aug 19 19:22 appv1.0
[root@demo-c8 data]# ll /tmp/appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:22 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f9.txt
[root@demo-c8 data]# rm -rf /tmp/*
[root@demo-c8 data]# ll /tmp
total 0
[root@demo-c8 data]# cp -av appv1.0 /tmp
'appv1.0' -> '/tmp/appv1.0'
'appv1.0/f1.txt' -> '/tmp/appv1.0/f1.txt'
'appv1.0/f2.txt' -> '/tmp/appv1.0/f2.txt'
'appv1.0/f3.txt' -> '/tmp/appv1.0/f3.txt'
'appv1.0/f4.txt' -> '/tmp/appv1.0/f4.txt'
'appv1.0/f5.txt' -> '/tmp/appv1.0/f5.txt'
'appv1.0/f6.txt' -> '/tmp/appv1.0/f6.txt'
'appv1.0/f7.txt' -> '/tmp/appv1.0/f7.txt'
'appv1.0/f8.txt' -> '/tmp/appv1.0/f8.txt'
'appv1.0/f9.txt' -> '/tmp/appv1.0/f9.txt'
[root@demo-c8 data]# ll /tmp
total 0
drwxr-xr-x. 2 root root 132 Aug 19 19:22 appv1.0
[root@demo-c8 data]# ll /tmp/appv1.0/
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:22 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f9.txt
[root@demo-c8 data]# rm -rf /tmp/*
[root@demo-c8 data]# ll /tmp
total 0
[root@demo-c8 data]# cp -av appv1.0/* /tmp
'appv1.0/f1.txt' -> '/tmp/f1.txt'
'appv1.0/f2.txt' -> '/tmp/f2.txt'
'appv1.0/f3.txt' -> '/tmp/f3.txt'
'appv1.0/f4.txt' -> '/tmp/f4.txt'
'appv1.0/f5.txt' -> '/tmp/f5.txt'
'appv1.0/f6.txt' -> '/tmp/f6.txt'
'appv1.0/f7.txt' -> '/tmp/f7.txt'
'appv1.0/f8.txt' -> '/tmp/f8.txt'
'appv1.0/f9.txt' -> '/tmp/f9.txt'
[root@demo-c8 data]# ll /tmp
total 0
-rw-r--r--. 1 root root 0 Aug 19 19:22 f1.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f2.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f3.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f4.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f5.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f6.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f7.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f8.txt
-rw-r--r--. 1 root root 0 Aug 19 19:22 f9.txt
readlink: 判断软链接指向的原始文件, 后面必须跟的是软链接, 否则没有返回结果, 脚本中会使用, 查看软链接指向的原始文件路径
针对目录创建软链接时, 源目录和目标目录都无需/
补全, 删除软链接目录时, 不要补全/
3.4 硬链接和软链接区别总结
- 本质
硬链接: 本质是同一个文件, 起了多个名字
软链接: 本质不是同一个文件
- 跨设备(磁盘分区)
硬链接: 不支持
软链接: 支持
- inode
硬链接: inode相同, 因为本质是同一个文件
软链接: inode不同, 因为本质不是同一个文件
- 链接数
硬链接: 创建新的硬链接, 链接数会增加, 删除硬链接, 链接数减少
软链接: 创建或删除, 链接数不会变化
一个文件的链接数, 指的是硬链接数量, 也就是一个文件有几个名
- 文件夹
硬链接: 不支持
软链接: 支持
- 相对路径
硬链接: 原始文件相对路径是相对于当前工作目录
软链接: 原始文件的相对路径是相对于链接文件的相对路径
- 删除源文件
硬链接: 只是链接数减1, 但是链接文件的访问不受影响
软链接: 链接文件将无法访问
- 文件类型
硬链接: 和源文件相同
软链接: 链接文件和源文件无关
I/O重定向
1. I/O重定向
1.1 标准输入和输出
程序: 指令+数据
读入数据: Input
输出数据: Output
文件描述符: 打开的文件都有一个fd, file descriptor
Linux给程序提供了三个设备, 标准输入, 标准输出, 标准错误, 用文件描述符0,1,2 分别表示, 这三个描述符对应的设备实际是由当前终端窗口实现, 依靠终端窗口来实现, 输入, 输出和错误
当前终端窗口不同, 文件描述符指向的窗口也不同
因此每个窗口都是有独立的文件描述符, 每个窗口内操作的命令不影响其他窗口
- 标注输入(STDIN) - 0 默认接受来自终端窗口的输入
- 标准输出(STDOUT) - 1 默认输出到终端窗口
- 标准错误(SRDERR) - 2 默认输出到终端窗口
范例: 文件描述符
[root@demo-c8 ~]# ll /dev/std*
lrwxrwxrwx. 1 root root 15 Aug 19 11:41 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx. 1 root root 15 Aug 19 11:41 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx. 1 root root 15 Aug 19 11:41 /dev/stdout -> /proc/self/fd/1
[17:07:05 root@centos8-2 ~]#ll /dev/std*
lrwxrwxrwx 1 root root 15 Aug 18 16:34 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Aug 18 16:34 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Aug 18 16:34 /dev/stdout -> /proc/self/fd/1
[17:07:11 root@centos8-2 ~]#ll /proc/self/fd
total 0
lrwx------ 1 root root 64 Aug 18 17:07 0 -> /dev/pts/0 # 当前终端的0,1,2标准输入, 输出, 错误都是指向终端pts/0
lrwx------ 1 root root 64 Aug 18 17:07 1 -> /dev/pts/0
lrwx------ 1 root root 64 Aug 18 17:07 2 -> /dev/pts/0
lr-x------ 1 root root 64 Aug 18 17:07 3 -> /var/lib/sss/mc/passwd
lrwx------ 1 root root 64 Aug 18 17:07 4 -> 'socket:[32968]'
lr-x------ 1 root root 64 Aug 18 17:07 5 -> /var/lib/sss/mc/group
lr-x------ 1 root root 64 Aug 18 17:07 6 -> /proc/1449/fd
[17:07:14 root@centos8-2 ~]#tty
/dev/pts/0 # 当前使用的终端是pts/0, 所以, 在当前终端执行的命令的结果, 已经输入的命令都是只显示在当前终端
[root@demo-c8 ~]# ls # ls的输出为标准输出, fd=1, 显示在当前终端
anaconda-ks.cfg Desktop Documents Downloads initial-setup-ks.cfg Music Pictures Public Templates Videos
[root@demo-c8 ~]# rm anaconda-ks.cfg
rm: remove regular file 'anaconda-ks.cfg'? n # rm的提示为标准输入, 需要用户在当前终端输入信息
[root@demo-c8 ~]# xxx
bash: xxx: command not found... # 错误信息为标准错误, 也是显示在当前终端
Failed to search for file: cannot update repo 'AppStream': Cannot prepare internal mirrorlist: No URLs in mirrorlist
- 执行命令后的提示不一定就是标准输出, 比如rm命令提示是否确认删除, 不算标准输出, 因为标准输出是可以重定向到文件中的, > test.txt
[17:17:49 root@centos8-2 ~]#rm anaconda-ks.cfg > /dev/pts/1
rm: remove regular file 'anaconda-ks.cfg'?
- 标准错误也不一定只包含错误提示, rm的提示就属于标准错误, 可以用2>重定向, 这需要根据程序开发时的定义来确定
[root@demo-c8 data]# rm test.txt 2> /dev/null
y
[root@demo-c8 data]# ll
total 8
-rw-r--r--. 1 root root 60 Aug 19 20:48 all.log
-rw-r--r--. 1 root root 732 Aug 19 20:49 all.txt
- 执行命令正常显示信息, 比如wget和curl的返回信息也被加到了错误输出, 但他们不是错误
1.2 重定向
重定向就是把输入,输出或者错误对应的设备进行改变, 由原本的窗口比如pts/0, 转移到pts/1, 这就是重定向. 重定向也可以把输入输出和错误, 定向到文件中
- 修改默认的输出, 输入, 错误显示窗口
[17:07:17 root@centos8-2 ~]#tty
/dev/pts/0
[17:15:45 root@centos8-2 ~]#hostname > /dev/pts/1
[17:15:58 root@centos8-2 ~]#
[17:09:31 root@centos8-2 ~]#tty
/dev/pts/1
[17:15:48 root@centos8-2 ~]#centos8-2.linux
- 可以重定向三个设备, 输出, 输入, 错误
1.2.1 标准输出和错误重定向
STDOUT和STDERR可以被重新定向到指定文件, 而非默认的当前终端
格式:
命令 操作符合 文件名
支持的操作符号包括:
1> 或 > 把标准输出STDOUT重定向到指定文件
2> 把标准错误STDERR重定向到指定文件
&> 把标准输出和错误都重定向到指定文件
>& 和&>功能一样, 建议用&>
- 标准输出和错误输出分别重定向到不同的文件
[17:27:18 root@centos8-2 /data/prac]#clear
[17:27:19 root@centos8-2 /data/prac]#ls /data /dddd > r.txt 2> w.txt
[17:27:46 root@centos8-2 /data/prac]#cat r.txt
/data:
prac
scripts
testing_scripts
[17:27:48 root@centos8-2 /data/prac]#cat w.txt
ls: cannot access '/dddd': No such file or directory
- 如果重定向的文件存在, 那么默认会被文件内容覆盖
set -C 禁止覆盖, 但可以追加, 不过利用 >| 操作符仍可以强制覆盖
set +C 允许覆盖, 默认就是允许覆盖
-
追加: >>, 在原有内容基础上, 追加新的内容
-
把输出和错误重新定向追加到文件中
>> 追加标准输出重定向到文件
2>> 追加标准错误重定向到文件
- 合并标准输出和错误输出为同一个数据流然后进行重定向
&> 覆盖重定向
&>> 追加重定向
- 顺序书写带来的影响
CMD > test.txt 2>&1
CMD 2> test.txt 1>&2
CMD &> test.txt
vs
CMD 2>&1 > test.txt
前三种实现的效果相同, 都是把输出和错误重定向到test.txt文件中
先把标椎输出重定向到一个文件里, 这样这个文件就生成了
然后再把错误输出重定向到标准输出, 此前标准输出已经是被重定向到test.txt文件
因此这个顺序可以实现标准输出和错误输出都重定向到test.txt文件
第四种写法会有不同结果
如果先把错误输出重定向到标准输出, 这时候还没有文件存放标准输出
因此错误输出直接打印在屏幕, 之后 > test.log将标准输出重定向到文件里
所以这种方法, 错误会直接输出在终端, 只有输出被重定向到了test.txt

[17:27:50 root@centos8-2 /data/prac]#ls /opt /xxx > test1.log 2>&1
[17:33:02 root@centos8-2 /data/prac]#cat test1.log
ls: cannot access '/xxx': No such file or directory
/opt:
[17:33:05 root@centos8-2 /data/prac]#ls /opt /xxx 2>&1 > test1.log
ls: cannot access '/xxx': No such file or directory
[17:33:18 root@centos8-2 /data/prac]#cat test1.log
/opt:
范例: 标准错误重定向
[root@demo-c8 ~]# rm /data/f1.log 2> /data/all.log # 重定向的文件会自动生成
[root@demo-c8 ~]# cat /data/all.log
rm: cannot remove '/data/f1.log': No such file or directory
范例: 合并多个命令的结果到一个文件
# 方法1: 小括号
[root@demo-c8 ~]# (cat /etc/issue; cat /etc/fstab) > /data/all.txt
[root@demo-c8 ~]# cat /data/all.txt
\S
Kernel \r on an \m
#
# /etc/fstab
# Created by anaconda on Mon Aug 15 16:52:19 2022
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=b1ab1ace-2582-4afd-8693-39bd9855041c / xfs defaults 0 0
UUID=d5131695-82b3-4a23-bc28-5c8a4bf381a0 /boot ext4 defaults 1 2
UUID=bdd66510-e510-4fe7-ba71-e2a35e6dc492 /data xfs defaults 0 0
UUID=05c944fb-d6f9-4544-ba10-8b7bf3cc8fed swap swap defaults 0 0
# 方法2: 花括号
[root@demo-c8 ~]# { ls;hostname; } > /data/test.txt
[root@demo-c8 ~]# cat /data/test.txt
anaconda-ks.cfg
Desktop
Documents
Downloads
initial-setup-ks.cfg
Music
Pictures
Public
Templates
Videos
demo-c8.demo
范例: 清空大文件
cat /dev/null /data/big_file.log
范例: 输出和错误分别重定向
ls /data /xxx > /data/out.log 2> /data/err.log
1.2.2 标准输入重定向
从文件中导入STDIN, 代替当前终端的输入设备, 使用<来重定向标准输入, 某些命令能够接受从文件中导入的STDIN
当命令执行时, 需要手动输入额外信息时, 可以借助标准输入重定向, 把需要手动输入的信息, 放到一个文件里, 通过标准输入把信息传递给执行的命令. 把人机交互的命令,变成非交互式批量执行
使用标准输入, 要求命令接受标准输入
# rm支持标准输入
[root@demo-c8 data]# echo y > rm.txt
[root@demo-c8 data]# rm aa.txt < rm.txt
rm: remove regular file 'aa.txt'? [root@demo-c8 data]# ll
total 28
-rw-r--r--. 1 root root 60 Aug 19 20:48 all.log
-rw-r--r--. 1 root root 732 Aug 19 20:49 all.txt
-rw-r--r--. 1 root root 7 Aug 19 21:35 a.txt
-rw-r--r--. 1 root root 5 Aug 19 21:40 b.txt
-rw-r--r--. 1 root root 2 Aug 19 21:48 rm.txt
-rw-r--r--. 1 root root 52 Aug 19 21:26 tr.log
-rw-r--r--. 1 root root 4 Aug 19 21:24 tr.txt
1.2.2.1 bc命令
[19:58:56 root@centos8-2 /data/prac]#echo 1+2+3+4 > bc.txt
[20:05:15 root@centos8-2 /data/prac]#bc < bc.txt
10
[20:05:46 root@centos8-2 /data/prac]#cat < bc.txt > bc2.txt
[20:05:51 root@centos8-2 /data/prac]#cat bc2.txt
1+2+3+4
连续数字求和
seq -s+ NUMBER
Usage: seq [OPTION]... LAST
or: seq [OPTION]... FIRST LAST
or: seq [OPTION]... FIRST INCREMENT LAST
seq
-s, --separator=STRING use STRING to separate numbers (default: \n)
[20:07:19 root@centos8-2 /data/prac]#seq -s+ 100
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
[20:08:21 root@centos8-2 /data/prac]#seq -s+ 100 | bc
5050
[root@demo-c8 data]# seq -s+ 100 > cal.txt
[root@demo-c8 data]# bc < cal.txt
5050
1.2.2.2 cat命令
cat支持标准输入重定向, 读进来什么, 就输出什么
[root@demo-c8 data]# seq -s+ 100 > a.txt
[root@demo-c8 data]# cat < a.txt > b.txt # 把a文件内容读到cat里, 再输出给b
[root@demo-c8 data]# cat b.txt
1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23+24+25+26+27+28+29+30+31+32+33+34+35+36+37+38+39+40+41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+59+60+61+62+63+64+65+66+67+68+69+70+71+72+73+74+75+76+77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100
- cat < a.file > a.file 这样会清空a文件, 不能同时读和写同一个文件
[20:13:37 root@centos8-2 /data/prac]#cat a.txt
Tue Aug 18 19:58:54 AEST 2020
root
root pts/0 2020-08-18 16:36 (10.0.0.1)
[20:13:39 root@centos8-2 /data/prac]#cat < a.txt > a.txt
[20:13:46 root@centos8-2 /data/prac]#cat a.txt
[20:13:48 root@centos8-2 /data/prac]#
1.2.2.3 tr命令
tr命令用于转换和删除字符, 接受标准输入
tr [OPTION]... SET1 [SET2]
SET1表示第一字符串, 包含要替换哪些数字和字母, SET2为第二字符集, 包含修改后的字母或数字
SET1和SET2的字符或数字, 一一对应修
默认, 如果SET1的个数大于SET2, 那么SET1的最后一位会取SET2最后一位的值. 如果加了-t选项, 那么就是一一对应修改, 多出来的字符不会被修改
[:digit:] 相当于0-9, 表示任意单个数字, 范围是0-9
[:lower:] 任意一个小写字母, 和[a-z]不等价
[:upper:] 任意一个大写字母, 和[A-Z]不等价
[:alpha:] 任意一个大写或小写字母
[:blank:] 任意一个水平空白字符
[:space:] 任意一个水平或垂直空白字符
[:punct:] 任意一个标点符号
[:print:] 任意一个可打印字符
[:cntrl:] 任意一个控制(非打印)字符
[:graph:] 图形字符
[:xdigit:] 任意一个16进制字符
- tr -c
显示指定字符集的补集
范例: tr不加选项, 可以实现字符替换, 1-a, 2-b, 3-c, 默认从标准输入读取数据, 输出到标准输出
[20:13:48 root@centos8-2 /data/prac]#tr 123 abc
1a2b3c
aabbcc
1A2B3C
aAbBcC
# 大小写转换
[root@demo-c8 data]# echo {a..z} > lower.txt
[root@demo-c8 data]# tr [a-z] [A-Z] < lower.txt > upper.txt # tr中[a-z]表示小写, [A-Z]是大写, 这里并不是通配符, 因为通配符针对的是文件, 而tr是针对标准输入传进来的内容做修改.
# tr的SET字符集有多种写法, 可以是[a-z], 也可以是'a-z', 都表示范围内的字符
[root@demo-c8 data]# cat upper.txt
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
# 当需要替换的字符个数, 大于替换后的字符个数时, 被替换的最后一个字符的值会按照替换后的最后一个字符取值, 这里4会变成c
[root@demo-c8 data]# tr 1234 abc
1234
abcc
# tr接受标准输入重定向, 可以把输入的内容, 写到文件里, 然后通过标准输入, 传给tr命令. 但此时, tr仅仅是把标准输入里的字符, 进行转换, 然后显示到标准输出上, 并不会修改标准输入的文件内容
[root@demo-c8 data]# cat tr.txt
ABC
[root@demo-c8 data]# tr ABC 123 < tr.txt
123
[root@demo-c8 data]# cat tr.txt
ABC
[20:27:09 root@centos8-2 /data/prac]#echo {a..z} > tr.log
[20:28:09 root@centos8-2 /data/prac]#tr [a-z] [A-Z] < tr.log
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
- tr -d abc
把标准输入中的abc删除, 如果通过文件标准输入传个tr, 那么并不会真把文件内容修改, 只是转换或者删除后显示在屏幕而已
[20:28:30 root@centos8-2 /data/prac]#tr -d abc < tr.log
d e f g h i j k l m n o p q r s t u v w x y z
- tr -s
把重复且连续的字符(数字和字母和符号)压缩成单个字符, 通常用于压缩空格, 比如配合df命令
[20:30:56 root@centos8-2 /data/prac]#tr -s abc # 只会压缩连续重复的a, b和c
aaaabbbbccccggggaaakkk
abcggggakkk
[20:33:06 root@centos8-2 /data/prac]#tr -s ab12AB
11223311212AABBABA
12331212ABABA
[20:34:25 root@centos8-2 /data/prac]#df | tr -s " " # 压缩空格
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 904560 0 904560 0% /dev
tmpfs 921340 0 921340 0% /dev/shm
tmpfs 921340 8912 912428 1% /run
tmpfs 921340 0 921340 0% /sys/fs/cgroup
/dev/sda2 104806400 2392860 102413540 3% /
/dev/sda5 101660164 741948 100918216 1% /data
/dev/sda1 999320 137624 792884 15% /boot
tmpfs 184268 0 184268 0% /run/user/0
[20:34:45 root@centos8-2 /data/prac]#tr -s " " < df.log
Filesystem 1K-blocks Used Available Use% Mounted on
devtmpfs 904560 0 904560 0% /dev
tmpfs 921340 0 921340 0% /dev/shm
tmpfs 921340 8912 912428 1% /run
tmpfs 921340 0 921340 0% /sys/fs/cgroup
/dev/sda2 104806400 2392860 102413540 3% /
/dev/sda5 101660164 741948 100918216 1% /data
/dev/sda1 999320 137624 792884 15% /boot
tmpfs 184268 0 184268 0% /run/user/0
利用tr -d把回车键\r删除,这样Windows格式就变成了Linux格式

[root@demo-c8 data]# cat a.txt
a
b
c[root@demo-c8 data]# file a.txt
a.txt: ASCII text, with CRLF line terminators # CRLF表示带有回车0d(\r)的Windows文件
[root@demo-c8 data]# tr -d '\r' < a.txt > b.txt
[root@demo-c8 data]# cat b.txt
a
b
c[root@demo-c8 data]# file b.txt
b.txt: ASCII text
[root@demo-c8 data]# hexdump -C a.txt
00000000 61 0d 0a 62 0d 0a 63 |a..b..c|
00000007
[root@demo-c8 data]# hexdump -C b.txt
00000000 61 0a 62 0a 63 |a.b.c|
00000005
- 利用tr命令, 将Linux文件改为Windows文件
1.2.2.4 标准输入单行重定向
利用cat命令实现, 每输入一行,摁回车,输入信息就重定向到文件里
单行重定向只能实现一次重定向一行, 无法批量重定向
-
输入第一行, 按回车, 查看f1.txt
image.png
image.png
-
输入第二行, 按回车, 查看f1.txt
image.png
image.png
1.2.2.5 多行重定向
cat命令也可以实现多行重定向, 多行重定向的终止符可以是任何字符, 但是前后必须完全一致
下面案例中, 终止符是efo, 但其可以是任何字符组合, 但是最后一行的结束符必须和起始一致且不能有额外符号, 比如空格
# 默认情况, 会把输入的多行数据, 显示在标准输出
[20:50:29 root@centos8-2 /data/prac]#cat <<efo
> 123
> 456
> efo
123
456
# 也可以把输入的多行数据, 输出到文本
[root@demo-c8 data]# cat > f2.txt <<EOF
> line1
> line2
> line3
> line4
> EOF
[root@demo-c8 data]# cat f2.txt
line1
line2
line
line4
范例: 生成配置文件, 一般直接写到脚本里
[20:58:15 root@centos8-2 /data/prac]#cat > httpd.cnf <<EOF
> [httpd]
> dir=/data
> EOF
[20:58:31 root@centos8-2 /data/prac]#cat httpd.cnf
[httpd]
dir=/data
[20:58:15 root@centos8-2 /data/prac]#cat > httpd.cnf <<-EOF #在脚本里,可以加个-解决文本对齐问题
- mail命令也支持标准输入重定向, 可以在Linux上向外发邮件
在Linux中利用特定邮箱往外发邮件
需要Linux安装mailx
包, 然后再发件邮件上开启授权, 这里演示利用QQ邮箱向外发邮件,这样就可以以指定的邮箱给任何邮箱发邮件了
[20:58:35 root@centos8-2 /data/prac]#mail -s IloveLinux abc@123.com
i love linux
ha ha ha
. #'.'是邮件结束符
EOT
[21:03:44 root@centos8-2 /data/prac]#No configuration file found at /root/.esmtprc or /etc/esmtprc
如果只像上面那样操作是发不出去的, 因为Linux发邮件,需要特定的服务
而Linux收的邮件是存在 /var/spool/mail/ 对应用户目录下
- 这里演示利用QQ邮箱向外发邮件
-
登录到QQ邮箱, 获取发邮件的授权码
-
编辑.mairc文件, 放到用户家目录下, 这样只有对应的用户登录时才能使用发邮件的功能, 放到/etc/mail.rc则对所有用户有效
不过, 这个要看邮件应用是以谁的身份来运行, 比如mailx是以root身份运行, 那么不管是把.mailrc放在root家目录还是其他用户家目录, 其他用户都可以利用mail发邮件,因为最终运行程序的是root, mailx会调用root家目录下的mailrc文件. 如果应用是以别的用户身份运行, 那么就会去对应的用户家目录找mailrc文件. 如果想对所有用户都有效, 就放到/etc/mail.rc
[21:23:36 root@centos8-2 ~]#cat .mailrc
set from=abc@123.com #邮件以哪个地址发送
set smtp=smtp.qq.com #腾旭qq邮箱服务器
set smtp-auth-user=abc@123.com #qq邮箱
set smtp-auth-password=fpwmwpjcjcmobfad #授权码
set smtp-auth=login
set smtp-verity=ignore
- 安装mailx包, 有了这个包才能使用mail命令
[21:28:11 root@centos8-2 ~]#yum provides mailx
Last metadata expiration check: 1:39:26 ago on Tue 18 Aug 2020 07:48:53 PM AEST.
mailx-12.5-29.el8.x86_64 : Enhanced implementation of the mailx command
Repo : @System
Matched from:
Provide : mailx = 12.5-29.el8
yum -y install mailx
mailx-12.5-29.el8.x86_64 : Enhanced implementation of the mailx command
Repo : BaseOS
Matched from:
Provide : mailx = 12.5-29.el8
[21:28:19 root@centos8-2 ~]#rpm -ql mailx
/bin/mail
/bin/mailx
/etc/mail.rc
...
- 发邮件
[21:28:57 root@centos8-2 ~]#echo linuxtest > mail.txt
[21:30:16 root@centos8-2 ~]#mail -s testmail abc@123.com < mail.txt # -s: 指定邮件标题
或者利用多行重定向
[21:31:55 root@centos8-2 ~]#mail -s testmail2 abc@123.com << EOF
> i love linux
> ha ha ha
> EOF
- 以.rc结尾的配置文件, 可以理解为run command, 运行命令
2. 管道
- 管道: 把前一个命令的标准输出, 作为后一个命令的标准输入
格式: CMD 1 | CMD 2 | CMD3...
CMD 1必须有标准输出, 否则无法传给管道,因为管道只接收标准输出
CMD 2必须支持标准输入
原本需要标准输入才能执行的命令, 可以利用管道, 将输入信息, 通过管道传给命令, 也就是原本需要标出输入信息才能执行的命令, 都可以通过管道把信息传给该命令, 不需要人机交互执行
[root@demo-c8 data]# seq -s+ 100 | bc
5050
cat /etc/issue | mail -s lalala abc@123.com
[root@demo-c8 data]# echo hello | tr 'a-z' 'A-Z'
HELLO
或者如果不确定前面命令是否会有错误输出, 可以使用 CMD
2>&1 | tr 'a-z' 'A-Z', 把错误先重定向到标准输出,再传给管道
或者 使用xxx |& tr 'a-z' 'A-Z'
[10:17:30 root@centos8-2 ~]#xxx | tr 'a-z' 'A-Z' #正常情况, 错误输出不会传给管道. 因为如果传给管道了, 那么小写字母就会被tr转为大写了
-bash: xxx: command not found
[10:17:18 root@centos8-2 ~]#xxx 2>&1 | tr 'a-z' 'A-Z #添加了 2>&1将错误输出先重'定向到标准输出, 再传给管道. 这样错误输出也可以被传个管道做处理
-BASH: XXX: COMMAND NOT FOUND
[10:17:23 root@centos8-2 ~]#xxx |& tr 'a-z' 'A-Z' #添加了 |& 将错误输出先重定向到标准输出, 再传给管道
-BASH: XXX: COMMAND NOT FOUND
管道可以多次处理
- 管道更改密码
# 该--stdin仅在红帽系统支持
[root@demo-c8 data]# echo "0000" | passwd --stdin wang
Changing password for user wang.
passwd: all authentication tokens updated successfully.
[10:21:45 root@centos8-2 ~]#xxx |& tr 'a-z' 'A-Z' | tr -d ':'
-BASH XXX COMMAND NOT FOUND
- Ubuntu系统可以使用chpasswd命令来修改密码
echo wang:000000 | chpasswd
- tee 命令
重定向和管道仅能实现单一的结果展示方式, 要么把结果输出到标准输出, 在终端展示, 要么重定向到文件里
tee命令既能把输出, 重定向到文件, 也能把输出打印在屏幕上
想把命令结果重定向到文件, 也想看看命令输出结果是什么, 可以用tee命令
如果输出文件存在, 默认会覆盖, tee -a
则追加
格式:
CMD | tee FILE/TO/PATH
[10:29:36 root@centos8-2 ~]#ls | tee /data/prac/ls.log
anaconda-ks.cfg
mail.txt
test.txt
[10:29:42 root@centos8-2 ~]#cat /data/prac/ls.log
anaconda-ks.cfg
mail.txt
test.txt
eg: 利用cat 多行重定向, 编辑文件, 既能把标准输入重定向到文件, 也能在屏幕看到输入内容
[10:29:50 root@demo-c8 ~]#cat << EOF > file.log
> ert
> dfg
> EOF
[root@demo-c8 data]# cat file.log
ert
dfg
[10:32:43 root@demo-c8 ~]#cat << EOF | tee file1.log
> lalala
> hahaha
> ooo
> EOF
lalala
hahaha
ooo
[root@demo-c8 data]# cat file1.log
lalala
hahaha
ooo
- 重定向中
-
的作用, 把网络上的文件内容显示在屏幕上而非下载到文件里
[10:52:07 root@centos8-2 ~]#curl http://abc.123.com/testdir/hello.sh #curl是专门的字符界面浏览器命令
#!/bin/bash
#经典写法
echo "hello, world"
#流行写法
echo 'Hello, world!'
# wget不加选项, 默认把文件下载到当前目录, 同时输出wget命令的输出到屏幕
# -q不显示wget自己的输出信息
# -O 支持把下载的内容写到文档或输出到屏幕, -O -, 把内容打印到标准输出, -O FILE, 输出到指定文件, 文件名可以自己指定
# -O - 文件内容输出到屏幕
# -P 把文件下载到指定目录, 无法修改源文件名
[10:54:10 root@centos8-2 ~]#wget -qO - http://abc.123.com/testdir/hello.sh # wget -qO - 间接实现浏览器功能, 把网上的文件显示到终端
#!/bin/bash
#经典写法
echo "hello, world"
#流行写法
echo 'Hello, world!'
[root@demo-c8 data]# wget -O - http://abc.123.com/testdir/hello.sh 2>/dev/null # 把wget命令本身的输出信息重定向到/dev/null
#!/bin/bash
#经典写法
echo "hello, world"
#流行写法
echo 'Hello, world!
- 练习
显示/etc目录下, 所有以l
开头, 以一个小写字母结尾, 且中间出现至少一位数字的文件或目录列表
ls /etc/l*[0-9]*[[:lower:]] -d
显示/etc目录下, 以任意一位数字开头, 且以非数字结尾的文件或目录列表
ls /etc/[0-9]*[^0-9] -d
显示/etc目录下, 以非字母开头, 后面跟了一个字母及其他任意长度任意字符的文件或目录列表
ls /etc/[^[:alpha:]][[:alpha:]]* -d
显示/etc目录下, 所有以rc
开头, 并且后面是0-6
之间的数字, 其他为任意字符的文件或目录列表
ls /etc/rc[0-6]* -d

显示/etc目录下, 所有以.conf
结尾, 且以m,n,r,p
开头的文件或目录列表
ls /etc/[mnrp]*.conf -d

只显示/root
下的隐藏文件和目录列表
ls /root/.* -d

只显示/etc
下的非隐藏目录列表
ls -ld /etc/[^.]* | grep ^d
ls -d /etc/[^.]*/ # Linux中, 目录是以/结尾的, 而文件的结尾没有/, 所以使用通配符管理文件和目录时, 可以按照结尾是否有/来区分文件和目录

计算, 1-100, 奇数和偶数之和
# seq START INCREMENT STOP
[root@demo-c8 data]# seq -s+ 1 2 100 | bc
2500
[root@demo-c8 data]# seq -s+ 2 2 100 | bc
2550
[root@demo-c8 data]# echo {1..100..2}
1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99
[root@demo-c8 data]# echo {1..100..2} | tr -s " " "+" | bc
2500
网友评论