如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
一 操作系统级 IO
1、虚拟文件系统
df 命令
检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。
❯ df -h
Filesystem Size Used Avail Use% Mounted on
udev 894M 0 894M 0% /dev
tmpfs 188M 1.1M 187M 1% /run
/dev/vda1 59G 9.6G 47G 18% /
tmpfs 940M 0 940M 0% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 940M 0 940M 0% /sys/fs/cgroup
tmpfs 188M 8.0K 188M 1% /run/user/116
tmpfs 188M 0 188M 0% /run/user/0
df filename
可以看到文件所在的块设备
du 命令
du 命令是对文件和目录磁盘使用的空间的查看
du
递归查看当前目录下的所有目录的空间占用
du -a
查看目录以及文件
fdisk
fdisk
是 Linux 的磁盘分区表操作工具。
-l
:输出后面接的装置所有的分区内容。若仅有fdisk -l
时, 则系统将会把整个系统内能够搜寻到的装置的分区均列出来。
mount 与 unmount
Linux 的磁盘挂载使用 mount
命令,卸载使用 umount
命令。
用默认的方式,将刚刚创建的 /dev/hdc6
挂载到 /mnt/hdc6
上面!
[root@www ~]# mkdir /mnt/hdc6
[root@www ~]# mount /dev/hdc6 /mnt/hdc6
[root@www ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
.....中间省略.....
/dev/hdc6 1976312 42072 1833836 3% /mnt/hdc6
磁盘卸载命令 umount
语法:
umount [-fn] 装置文件名或挂载点
选项与参数:
- -f :强制卸除!可用在类似网络文件系统 (NFS) 无法读取到的情况下;
- -n :不升级
/etc/mtab
情况下卸除。
卸载/dev/hdc6
[root@www ~]# umount /dev/hdc6
软连接与硬链接
硬链接:
ln 1.txt 2.txt
为文件 1.txt
创建硬链接 2.txt
❯ touch main.c
❯ ls
dou main.c tool work
❯ ln main.c hard_main.c
❯ ls
dou hard_main.c main.c tool work
❯ ls -l
total 12
drwxr-xr-x 4 root root 4096 Jan 4 16:51 dou
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
drwxr-xr-x 3 root root 4096 Jan 1 03:10 tool
drwxr-xr-x 2 root root 4096 Dec 9 16:00 work
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
main.c
和 hard_main.c
的引用变为了 2
❯ stat main.c
File: main.c
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fc01h/64513d Inode: 655656 Links: 2
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-05-15 19:53:32.870154505 +0800
Modify: 2022-05-15 19:53:32.870154505 +0800
Change: 2022-05-15 19:53:45.574415596 +0800
Birth: -
❯ stat hard_main.c
File: hard_main.c
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fc01h/64513d Inode: 655656 Links: 2
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-05-15 19:53:32.870154505 +0800
Modify: 2022-05-15 19:53:32.870154505 +0800
Change: 2022-05-15 19:53:45.574415596 +0800
Birth: -
两个文件 inode
一样
main.c
和hard_main.c
在磁盘上只有一份,但是在操作系统的虚拟文件系统中有不同的 path
修改 main.c
,hard_main.c
也会改变。删除 main.c
,hard_main.c
不受影响。
软连接:
ln -s 1.txt 2.txt
为文件 1.txt
创建软链接 2.txt
❯ ln -s main.c soft_main.c
❯ ls -l
total 12
drwxr-xr-x 4 root root 4096 Jan 4 16:51 dou
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
lrwxrwxrwx 1 root root 6 May 15 20:00 soft_main.c -> main.c
drwxr-xr-x 3 root root 4096 Jan 1 03:10 tool
drwxr-xr-x 2 root root 4096 Dec 9 16:00 work
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
lrwxrwxrwx 1 root root 6 May 15 20:00 soft_main.c -> main.c
main.c
和 hard_main.c
的引用还是 2
, soft_main.c
的引用为 1
❯ stat soft_main.c
File: soft_main.c -> main.c
Size: 6 Blocks: 0 IO Block: 4096 symbolic link
Device: fc01h/64513d Inode: 656326 Links: 1
Access: (0777/lrwxrwxrwx) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-05-15 20:00:59.203327075 +0800
Modify: 2022-05-15 20:00:57.451291069 +0800
Change: 2022-05-15 20:00:57.451291069 +0800
Birth: -
❯ stat main.c
File: main.c
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fc01h/64513d Inode: 655656 Links: 2
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-05-15 19:53:32.870154505 +0800
Modify: 2022-05-15 19:53:32.870154505 +0800
Change: 2022-05-15 19:53:45.574415596 +0800
Birth: -
main.c
和 soft_main.c
具有不同的 inode
号
软连接,也称符号链接。类似 windows 中的快捷方式
修改 soft_main.c
,实际上就是修改main.c
。删除 main.c
,soft_main.c
指向丢失,无法打开。
dd 命令
dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。
- if=文件名:输入文件名,默认为标准输入。即指定源文件。
- of=文件名:输出文件名,默认为标准输出。即指定目的文件。
- ibs=bytes:一次读入bytes个字节,即指定一个块大小为bytes个字节。 obs=bytes:一次输出bytes个字节,即指定一个块大小为bytes个字节。 bs=bytes:同时设置读入/输出的块大小为bytes个字节。
- cbs=bytes:一次转换bytes个字节,即指定转换缓冲区大小。
- skip=blocks:从输入文件开头跳过blocks个块后再开始复制。
- seek=blocks:从输出文件开头跳过blocks个块后再开始复制。
- count=blocks:仅拷贝blocks个块,块大小等于ibs指定的字节数。
- conv=<关键字>,关键字可以有以下11种: 关键字>
- conversion:用指定的参数转换文件。
- ascii:转换ebcdic为ascii
- ebcdic:转换ascii为ebcdic
- ibm:转换ascii为alternate ebcdic
- block:把每一行转换为长度为cbs,不足部分用空格填充
- unblock:使每一行的长度都为cbs,不足部分用空格填充
- lcase:把大写字符转换为小写字符
- ucase:把小写字符转换为大写字符
- swap:交换输入的每对字节
- noerror:出错时不停止
- notrunc:不截短输出文件
- sync:将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
将文件 lowercase.txt
中的所有小写字母编程大写字母并输出到文件 uppercase.txt
❯ dd if=lowercase.txt of=uppercase.txt conv=ucase
0+1 records in
0+1 records out
52 bytes copied, 0.000335669 s, 155 kB/s
❯ ls
dou hard_main.c lowercase.txt main.c soft_main.c tool uppercase.txt work
❯ cat lowercase.txt
Hello! This is an example for commamd dd.
Goodbye~~
❯ cat uppercase.txt
HELLO! THIS IS AN EXAMPLE FOR COMMAMD DD.
GOODBYE~~
losetup 命令
循环设备可把文件虚拟成区块设备,籍以模拟整个文件系统,让用户得以将其视为硬盘驱动器,光驱或软驱等设备,并挂入当作目录来使用。
(1)创建空的磁盘镜像文件,这里创建一个1.44M的软盘
$ dd if=/dev/zero of=floppy.img bs=512 count=2880
(2)使用 losetup将磁盘镜像文件虚拟成块设备
$ losetup /dev/loop1 floppy.img
(3)挂载块设备
$ mount /dev/loop0 /tmp
经过上面的三步之后,我们就可以通过/tmp目录,像访问真实块设备一样来访问磁盘镜像文件floppy.img。
mke2fs 命令
Linux mke2fs 命令用于建立 ext2 文件系统。
使用文件模拟磁盘并挂载
/dev/zero
在被读取时会提供无限的空字符(ASCII NUL, 0x00)
制作一个大小为 100M (100 个大小为 1M 的块)的文件
#制作文件
❯ dd if=/dev/zero of=boot.img bs=1048574 count=100
100+0 records in
100+0 records out
104857400 bytes (105 MB, 100 MiB) copied, 0.177023 s, 592 MB/s
❯ ls
boot.img dou hard_main.c lowercase.txt main.c soft_main.c tool uppercase.txt work
❯ ls -l boot.img
-rw-r--r-- 1 root root 104857400 May 16 00:30 boot.img
#将文件虚拟化为块设备
❯ losetup /dev/loop0 boot.img
losetup: boot.img: Warning: file does not fit into a 512-byte sector; the end of the file will be ignored.
#使用 ext2 格式化
❯ mke2fs /dev/loop0
mke2fs 1.45.5 (07-Jan-2020)
Discarding device blocks: done
Creating filesystem with 25599 4k blocks and 25600 inodes
Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
❯ mkdir /mnt/mydisk
❯ ls -l /mnt/mydisk
total 0
# 将 /dev/loop0 挂载到 /mnt/mydisk
❯ mount /dev/loop0 /mnt/mydisk
❯ df
Filesystem 1K-blocks Used Available Use% Mounted on
udev 915128 0 915128 0% /dev
tmpfs 192504 1084 191420 1% /run
/dev/vda1 61795304 10048704 49004104 18% /
...
/dev/loop0 99180 48 94016 1% /mnt/mydisk #新增了记录
❯ cd /mnt/mydisk
❯ ls -al
total 24
drwxr-xr-x 3 root root 4096 May 16 01:11 .
drwxr-xr-x 3 root root 4096 May 16 09:47 ..
drwx------ 2 root root 16384 May 16 01:11 lost+found
#增加zsh可执行程序
❯ whereis zsh
zsh: /usr/bin/zsh /usr/lib/x86_64-linux-gnu/zsh /etc/zsh /usr/share/zsh /usr/share/man/man1/zsh.1.gz
❯ mkdir -p usr/bin
❯ cp /usr/bin/zsh usr/bin/
❯ ls usr/bin
zsh
#增加依赖的库
❯ ldd bash
ldd: ./bash: No such file or directory
❯ ldd /usr/bin/zsh
linux-vdso.so.1 (0x00007fff20cfd000)
libcap.so.2 => /lib/x86_64-linux-gnu/libcap.so.2 (0x00007fc92cc4f000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fc92cc49000)
libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 (0x00007fc92cc19000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fc92caca000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc92c8d8000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc92cd59000)
❯ mkdir -p lib/x86_64-linux-gnu
❯ mkdir lib64
❯ cp /lib/x86_64-linux-gnu/{libcap.so.2,libdl.so.2,libtinfo.so.6,libc.so.6,libm.so.6} lib/x86_64-linux-gnu/
❯ cp /lib64/ld-linux-x86-64.so.2 lib64
# 切换根目录到 /mnt/mydisk
❯ chroot ./
zsh: failed to load module `zsh/zle': /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/zle.so: cannot open shared object file: No such file or directory
iZuf66sz7dk0i1hksp2tlaZ# ls
zsh: command not found: ls
#在根目录下创建文件 test.txt
iZuf66sz7dk0i1hksp2tlaZ# echo "test" > test.txt
iZuf66sz7dk0i1hksp2tlaZ# echo $$
60130
iZuf66sz7dk0i1hksp2tlaZ# exit
# 进程号不同
❯ echo $$
58762
# test.txt 出现在了 /mnt/mydisk 目录下而未出现在 / 目录下
❯ ls
lib lib64 lost+found test.txt usr
❯ ls /
bin dev home lib32 libx32 media opt root sbin srv sys test.c usr
boot etc lib lib64 lost+found mnt proc run snap swapfile test tmp var
lsof 命令
lsof
-p
查看进程打开文件-o
查看文件的 offset
lsof 命令不仅可以查看进程打开的文件、目录,还可以查看进程监听的端口等 socket 相关的信息
FD 列中的常见内容有 cwd、rtd、txt、mem 和一些数字等等。其中 cwd 表示当前的工作目录;rtd 表示根目录;txt 表示程序的可执行文件;mem 表示内存映射文件。
u
表示文件为读写,r
为读取,w
为写入
lsof -p process_id
查看进程打开的文件:
❯ lsof -p $$
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/116/gvfs
Output information may be incomplete.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
zsh 60895 root cwd DIR 252,1 4096 655362 /root
zsh 60895 root rtd DIR 252,1 4096 2 /
zsh 60895 root txt REG 252,1 878288 1205122 /usr/bin/zsh
zsh 60895 root mem REG 252,1 198112 1708416 /usr/share/zsh/functions/Zle.zwc
zsh 60895 root mem REG 252,1 28008 1708542 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/files.so
zsh 60895 root mem REG 252,1 14632 1708548 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/net/socket.so
zsh 60895 root mem REG 252,1 14760 1708564 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/zleparameter.so
zsh 60895 root mem REG 252,1 14696 1708560 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/termcap.so
zsh 60895 root mem REG 252,1 25768 1708545 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/mathfunc.so
zsh 60895 root mem REG 252,1 202728 1708234 /usr/share/zsh/functions/Misc.zwc
zsh 60895 root mem REG 252,1 14624 1708555 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/regex.so
zsh 60895 root mem REG 252,1 19000 1708537 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/datetime.so
zsh 60895 root mem REG 252,1 294424 1707343 /usr/share/zsh/functions/Completion/Base.zwc
zsh 60895 root mem REG 252,1 18912 1708558 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/stat.so
zsh 60895 root mem REG 252,1 72136 1708534 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/complist.so
zsh 60895 root mem REG 252,1 32680 1708559 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/system.so
zsh 60895 root mem REG 252,1 15304 1708543 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/langinfo.so
zsh 60895 root mem REG 252,1 189744 1708185 /usr/share/zsh/functions/Completion.zwc
zsh 60895 root mem REG 252,1 49264 1708553 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/parameter.so
zsh 60895 root mem REG 252,1 39400 1708568 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/zutil.so
zsh 60895 root mem REG 252,1 155704 1708533 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/complete.so
zsh 60895 root mem REG 252,1 335808 1708563 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/zle.so
zsh 60895 root mem REG 252,1 51832 1195271 /usr/lib/x86_64-linux-gnu/libnss_files-2.31.so
zsh 60895 root mem REG 252,1 5699248 1180613 /usr/lib/locale/locale-archive
zsh 60895 root mem REG 252,1 2029224 1185459 /usr/lib/x86_64-linux-gnu/libc-2.31.so
zsh 60895 root mem REG 252,1 1369352 1185461 /usr/lib/x86_64-linux-gnu/libm-2.31.so
zsh 60895 root mem REG 252,1 192032 1182764 /usr/lib/x86_64-linux-gnu/libtinfo.so.6.2
zsh 60895 root mem REG 252,1 18816 1185460 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
zsh 60895 root mem REG 252,1 31120 1182572 /usr/lib/x86_64-linux-gnu/libcap.so.2.32
zsh 60895 root mem REG 252,1 14696 1708561 /usr/lib/x86_64-linux-gnu/zsh/5.8/zsh/terminfo.so
zsh 60895 root mem REG 252,1 27002 1195536 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
zsh 60895 root mem REG 252,1 191472 1182753 /usr/lib/x86_64-linux-gnu/ld-2.31.so
zsh 60895 root 0u CHR 136,0 0t0 3 /dev/pts/0
zsh 60895 root 1u CHR 136,0 0t0 3 /dev/pts/0
zsh 60895 root 2u CHR 136,0 0t0 3 /dev/pts/0
zsh 60895 root 10u CHR 136,0 0t0 3 /dev/pts/0
zsh 60895 root 11w FIFO 252,1 0t0 1182530 /tmp/gitstatus.POWERLEVEL9K.0.60895.1652688319.1.fifo (deleted)
zsh 60895 root 12r REG 252,1 189744 1708185 /usr/share/zsh/functions/Completion.zwc
zsh 60895 root 13w FIFO 252,1 0t0 1182538 /tmp/p10k.worker.0.60895.1652688319.fifo (deleted)
zsh 60895 root 14r REG 252,1 294424 1707343 /usr/share/zsh/functions/Completion/Base.zwc
zsh 60895 root 17r REG 252,1 198112 1708416 /usr/share/zsh/functions/Zle.zwc
zsh 60895 root 18r FIFO 0,13 0t0 8588796 pipe
zsh 60895 root 19r FIFO 0,13 0t0 8589443 pipe
zsh 60895 root 21r REG 252,1 202728 1708234 /usr/share/zsh/functions/Misc.zwc
其中查看到的文件描述符保存于目录 /proc/进程号/fd
❯ echo $$
60895
❯ ll /proc/60895/fd
total 0
lrwx------ 1 root root 64 May 16 16:05 0 -> /dev/pts/0
lrwx------ 1 root root 64 May 16 16:05 1 -> /dev/pts/0
lrwx------ 1 root root 64 May 16 16:05 10 -> /dev/pts/0
l-wx------ 1 root root 64 May 16 16:05 11 -> '/tmp/gitstatus.POWERLEVEL9K.0.60895.1652688319.1.fifo (deleted)'
lr-x------ 1 root root 64 May 16 16:05 12 -> /usr/share/zsh/functions/Completion.zwc
l-wx------ 1 root root 64 May 16 16:05 13 -> '/tmp/p10k.worker.0.60895.1652688319.fifo (deleted)'
lr-x------ 1 root root 64 May 16 16:05 14 -> /usr/share/zsh/functions/Completion/Base.zwc
lr-x------ 1 root root 64 May 16 16:05 17 -> /usr/share/zsh/functions/Zle.zwc
lr-x------ 1 root root 64 May 16 16:05 18 -> 'pipe:[8588796]'
lr-x------ 1 root root 64 May 16 16:05 19 -> 'pipe:[8589443]'
lrwx------ 1 root root 64 May 16 16:05 2 -> /dev/pts/0
lr-x------ 1 root root 64 May 16 16:05 21 -> /usr/share/zsh/functions/Misc.zwc
lr-x------ 1 root root 64 May 16 16:05 8 -> /root/file
exec 命令
# 将文件 file 与文件描述符 8 绑定
❯ exec 8< file
❯ lsof -p $$
...
zsh 60895 root 8r REG 252,1 24 670217 /root/file
...
#从文件描述符 8 指向的文件中读取一行数据放入变量 a 中
❯ read a 0<&8
❯ echo $a
This
❯ read a 0<&8
❯ echo $a
is
❯ lsof -op $$ | grep /root/file
zsh 60895 root 8r REG 252,1 0t8 670217 /root/file
子进程会继承父进程的文件描述符表
❯ echo $$
60895
# 创建子进程
❯ zsh
❯ echo $$
61260
❯ lsof -op $$ | grep /root/file
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/116/gvfs
Output information may be incomplete.
# 8 文件描述符依然存在
zsh 61260 root 8r REG 252,1 0t8 670217 /root/file
#新启一个终端
❯ echo $$
61387
❯ lsof -op $$ | grep /root/file
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/116/gvfs
Output information may be incomplete.
# 8 找不到了
下面是通过 pstree
命令看到的进程关系图
├─sshd─┬─sshd───zsh───zsh───pstree
│ └─sshd───zsh #新打开的终端
bash 中的 /dev/tcp
Linux中的一个特殊文件: /dev/tcp 打开这个文件就类似于发出了一个socket调用,建立一个socket连接,读写这个文件就相当于在这个socket连接中传输数据。
/dev/tcp/host/port 其实是一个 bash 的 feature,由于是 bash的 feature,因此在别的 shell下就不能生效,所以需要注意使用shell类型。
root@iZuf66sz7dk0i1hksp2tlaZ:/proc# exec 9<> /dev/tcp/www.baidu.com/80
root@iZuf66sz7dk0i1hksp2tlaZ:/proc# lsof -p $$
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/116/gvfs
Output information may be incomplete.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
bash 61823 root 9u IPv4 8617541 0t0 TCP iZuf66sz7dk0i1hksp2tlaZ:50196->112.80.248.75:http (ESTABLISHED)
...
2、管道 pipeline
重定向
例 1
❯ ll > output #等价于 ll 1> output
❯ cat output
total 9.1M
-rw-r--r-- 1 root root 100M May 16 17:03 boot.img
drwxr-xr-x 4 root root 4.0K Jan 4 16:51 dou
-rw-r--r-- 1 root root 24 May 16 16:25 file
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 1 root root 52 May 16 00:26 lowercase.txt
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
-rw-r--r-- 1 root root 0 May 16 17:26 output
lrwxrwxrwx 1 root root 6 May 15 20:00 soft_main.c -> main.c
drwxr-xr-x 3 root root 4.0K Jan 1 03:10 tool
-rw-r--r-- 1 root root 52 May 16 00:27 uppercase.txt
drwxr-xr-x 2 root root 4.0K Dec 9 16:00 work
❯ cat output > output2 #等价于 cat 0< output 1> output2
❯ cat output2
total 9.1M
-rw-r--r-- 1 root root 100M May 16 17:03 boot.img
drwxr-xr-x 4 root root 4.0K Jan 4 16:51 dou
-rw-r--r-- 1 root root 24 May 16 16:25 file
-rw-r--r-- 2 root root 0 May 15 19:53 hard_main.c
-rw-r--r-- 1 root root 52 May 16 00:26 lowercase.txt
-rw-r--r-- 2 root root 0 May 15 19:53 main.c
-rw-r--r-- 1 root root 0 May 16 17:26 output
lrwxrwxrwx 1 root root 6 May 15 20:00 soft_main.c -> main.c
drwxr-xr-x 3 root root 4.0K Jan 1 03:10 tool
-rw-r--r-- 1 root root 52 May 16 00:27 uppercase.txt
drwxr-xr-x 2 root root 4.0K Dec 9 16:00 work
例 2:
❯ ls . dir
ls: cannot access 'dir': No such file or directory
.:
boot.img dou file hard_main.c lowercase.txt main.c soft_main.c tool uppercase.txt work
如果我们想让 ls
的标准输出和错误输出不显示到屏幕,而是显示到文件中
❯ ls . dir 1> stdout 2> stderr
❯ cat stdout
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
stderr
stdout
tool
uppercase.txt
work
❯ cat stderr
ls: cannot access 'dir': No such file or directory
当我们试图将标准输出和标准错误都重定向到同一个文件时:
❯ ls . dir 1> std 2> std
❯ cat std
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
std
stderr
stdout
tool
uppercase.txt
work
发现标准错误被覆盖了!这是因为文件 std 被打开了两次且每次都是覆盖写。
我们可以通过修改写文件的方式为追加解决这个问题:
❯ ls . dir 1>> std 2>> std
❯ cat std
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
std
stderr
stdout
tool
uppercase.txt
work
ls: cannot access 'dir': No such file or directory
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
std
stderr
stdout
tool
uppercase.txt
work
我们还可以将标准错误绑定到标准输出上,也可以解决覆盖问题,并且 std 文件只被打开了一次,效率更高。
❯ echo > std
❯ ls . dir 2>&1 1>std
ls: cannot access 'dir': No such file or directory
❯ cat std
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
std
stderr
stdout
tool
uppercase.txt
work
通过输出发现标准错误并没用重定向到 std 文件中,这是因为绑定操作存在顺序,标准错误2先绑定到文件描述符1也就是标准输出上,让后标准输出才重定向到 std 文件上。
正确的写法是:
❯ ls . dir 1>std 2>&1
❯ cat std
ls: cannot access 'dir': No such file or directory
.:
boot.img
dou
file
hard_main.c
lowercase.txt
main.c
soft_main.c
std
stderr
stdout
tool
uppercase.txt
work
{ }
命令 (命令块)
一次执行多条命令:
❯ { mkdir test; mv main.c test; cd test; }
❯ ls
main.c
❯ pwd
/root/test
管道
例 1
如何只显示文件中的第八行?
❯ cat file
line 1
line 2
line 3
line 4
line 5
line 6
line 7
line 8
line 8
❯ head -8 file | tail -1
line 8
例 2
创建一个变量设定一个初始值,然后通过带管道的命令行左侧表达式给 a 重新赋值,解释完该行命令后再次输出 a 的值,发现 a 并未改变。
❯ a=1
❯ echo $a
1
❯ { a=10; echo "hello"; } | cat
hello
❯ echo $a
1
管道
|
的左右两侧会各启动一个子进程分别解释命令,然后将管道左侧的标准输出重定向给管道,同时将右侧的标准输出重定向到管道,以此完成两个子进程间的数据传递。
因此,子进程中对变量 a 的修改无法改变父进程的变量的值。
在父进程中将 a 设为 export
:
❯ export a
❯ a=10 | a=9
❯ echo $a
9
$$ 与 $BASHPID
观察下面的运行结果:
❯ echo $$
64414
❯ echo $$ | cat
64414
按理第二次 echo
的输出应该为管道左侧子进程的 PID
,这里却和父进程一样!
这是因为 $$
的优先级高于管道创建子进程,这里将 $$
改为 $BASHPID
就可以正确的输出
例 3
❯ echo $$
62765
❯ { read a; echo "read a finish"; } | { read b; echo "read b finish"; }
❯
管道左右侧子进程阻塞
使用 ps -ef
查看进程:
❯ ps -ef | grep "62765"
root 62765 62640 0 20:36 pts/0 00:00:04 -zsh
root 65352 62765 0 21:11 pts/0 00:00:00 -zsh
进程 65352
为 62765
(主调进程)的子进程
查看进程65352打开的文件描述符:
❯ ll /proc/65352/fd
total 0
lrwx------ 1 root root 64 May 16 21:11 0 -> /dev/pts/0
l-wx------ 1 root root 64 May 16 21:11 1 -> 'pipe:[8685064]'
lrwx------ 1 root root 64 May 16 21:11 2 -> /dev/pts/0
标准输出 1 重定向到了管道
> lsof -p 65352
zsh 65352 root 0u CHR 136,0 0t0 3 /dev/pts/0
# FD 变为了只写 1w TYPE 为 FIFO
zsh 65352 root 1w FIFO 0,13 0t0 8685064 pipe
zsh 65352 root 2u CHR 136,0 0t0 3 /dev/pts/0
zsh 65352 root 10u CHR 136,0 0t0 3 /dev/pts/0