1. 重定向概述
1.1 什么是重定向
将原本要输出到屏幕的数据信息, 重新定向到某个指定的文件中.
比如:每天凌晨定时备份数据, 希望将备份数据的结果保存到某个文件中.
这样第二天通过查看文件的内容就知道昨天备份的数据是成功还是失败.
1.2 为何要使用重定向
1. 当屏幕输出的信息很重要, 而且希望保存重要的信息时
2. 后台执行中的程序, 不希望他干扰屏幕正常的输出结果时
3. 系统的例行命令, 例如定时任务的执行结果, 希望可以存下来时
4. 一些执行命令, 我们已经知道他可能出现错误信息, 想将他直接丢弃时
5. 错误日志与正确日志需要分别输出至不同的文件保存时
1.3 标准输入与输出
学习重定向的预备知识, 标准输入与输出
当运行一个程序时通常会自动打开三个标准文件, 分别是标准输入、标准输出、错误输出
| 名称 | 文件描述符 | 作用 |
|---|---|---|
| 标准输入(STDIN) | 0 | 默认是键盘, 也可以是文件或其他命令的输出. |
| 标准输出(STDOUT) | 1 | 默认输出到屏幕. |
| 错误输出(STDERR) | 2 | 默认输出到屏幕. |
| 文件名称(filename) | 3+ |
进程将从标准输入中得到数据, 将正常输出打印至屏幕终端, 将错误的输出信息也打印至屏幕终端.
PS: 进程是使用文件描述符(file descriptors)来管理打开的文件

1.4 cat命令实例
* 1. cat 命令的功能是从命令行给出的文件中读取数据, 并将这些数据直接送到标准输出.
# 会把文件/etc/passwd的内容输出显示到屏幕上
[root@kid ~]# cat /etc/passwd
* 2. cat 命令没有跟上输入的文件名, 那么cat命令则会通过命令行标准输入中读取数据, 并将其送到标准输出.
[root@kid ~]# cat
hello # 标准输入 (回车)
hello # 标准输出
^Z # ctrl + z
# 用户输入的每一行都立刻被cat命令输出到屏幕上.
* 3. 标准输入输出过程
# 持续追踪查看文件内容
[root@kid ~]# tail -f /etc/passwd
t1:x:1026:1037::/home/t1:/bin/bash
t2:x:1027:1038::/home/t2:/bin/bash
...
ctrl+z 将进程转到后台
# 查看运行的进程
[root@kid ~]# ps
PID TTY TIME CMD
9111 pts/0 00:00:00 bash
9128 pts/0 00:00:00 cat
9131 pts/0 00:00:00 tail
9133 pts/0 00:00:00 ps
# 查看tail命令的pid, 6885进程下的文件描述符
[root@kid ~]# ls -l /proc/9131/fd
total 0
lrwx------. 1 root root 64 Sep 2 20:33 0 -> /dev/pts/0
lrwx------. 1 root root 64 Sep 2 20:33 1 -> /dev/pts/0
lrwx------. 1 root root 64 Sep 2 20:32 2 -> /dev/pts/0
lr-x------. 1 root root 64 Sep 2 20:33 3 -> /etc/passwd
lr-x------. 1 root root 64 Sep 2 20:33 4 -> anon_inode:inotify
#Linux查看标准输入输出设备(0 , 1, 2)
[root@kid ~]# ll /dev/std*
lrwxrwxrwx. 1 root root 15 Sep 1 11:59 /dev/stderr -> /proc/self/fd/2
lrwxrwxrwx. 1 root root 15 Sep 1 11:59 /dev/stdin -> /proc/self/fd/0
lrwxrwxrwx. 1 root root 15 Sep 1 11:59 /dev/stdout -> /proc/self/fd/1
2. 输出重定向
输出重定向, 改变输出内容的位置.输出重定向有如下几种方式, 如表格所示:
| 类型 | 操作符 | 用途 |
|---|---|---|
| 标准覆盖输出重定向 | > | 将程序输出的正确结果输出到指定的文件中, 会覆盖文件原有的内容 |
| 标准追加输出重定向 | >> | 将程序输出的正确结果以追加的方式输出到指定文件, 不会覆盖原有文件 |
| 错误覆盖输出重定向 | 2> | 将程序的错误结果输出到执行的文件中, 会覆盖文件原有的内容 |
| 错误追加输出重定向 | 2>> | 将程序输出的错误结果以追加的方式输出到指定文件, 不会覆盖原有文件 |
| 标准输入重定向 | << | 将命令中接收输入的途径由默认的键盘更改为指定的文件或命令 |
2.1 覆写
> 操作符, 先清空, 后写入, 如果文件不存在则创建 .
* 每次都会覆盖文件内容

# 创建文件
[root@kid ~]# touch 1.txt
# 查看当前目录下的信息
[root@kid ~]# ll > ll.txt
# 发现有两个文件, 文件不存在先创建文件, 在执行ll命令, 再将结果输入到ll.txt文件中
[root@kid ~]# cat ll.txt
total 0
-rw-r--r--. 1 root root 0 Sep 2 20:45 1.txt
-rw-r--r--. 1 root root 0 Sep 2 20:45 ll.txt
2.2 追加
>> 操作符, 会往文件的尾部在添加内容, 文件不存在会新建.

# 标准追加输出重定向, 向配置文件末尾追加内容
[root@kid ~]# echo "hello word" >> helle.py
[root@kid ~]# cat helle.py
hello word
2.3 错误输出重定向
2> 操作符, 重定向错误的信息腹写到文本中, 文件不存在则创建

# 输入一个错误的命令
[root@kid ~]# lll
-bash: lll: command not found
# 终端不展示错误信息, 覆写到文本中
[root@kid ~]# lll 2> error.txt
# 查看文本信息
[root@kid ~]# cat error.txt
-bash: lll: command not found
2.4 重定向所有输出
&> 操作符, 将标准输出和标准错误输出重定向到同一个文件, 混合输出.

[root@kid ~]# cat ll &> re.txt
[root@kid ~]# cat re.txt
cat: ll: No such file or directory
# 文件合并
[root@kid ~]# echo '123' > a.txt
[root@kid ~]# echo '456' > b.txt
[root@kid ~]# cat a.txt b.txt > ab.txt
[root@kid ~]# cat ab.txt
123
456
2.5 错误信息重定向到标准输出
2&>1 操作符, 将错误的信息重定向到标准化输出的相同位置.

# 查看连个目录
[root@kid ~]# ls /root /error
# error 目录u存在则会报错
ls: cannot access /error: No such file or directory
# root 目录存在则打印目录下的文件信息
/root:
ab ab.txt a.txt b.txt re.txt
# 将正确的信息保存到文件中, 错误的信息展示到终端
[root@kid ~]# ls /root /error >ab
ls: cannot access /error: No such file or directory
# 查看ab文件中记录的正确信息
[root@kid ~]# cat ab
/root:
ab
ab.txt
a.txt
b.txt
re.txt
# 将正确的信息与错误的信息都保存到同一个文件中
[root@kid ~]# ls /root /error >ab 2>&1
[root@kid ~]# cat ab
ls: cannot access /error: No such file or directory
/root:
ab
ab.txt
a.txt
b.txt
re.txt
# 顺序好像是, 想将错误信息以正确的通道输出, 然后创建ab文件, 在执行ls命令
2.6 重定向到空设备
/dev/null 目录放入任何信息都会消失

# 将产生的任何数据放入黑洞设备, 则视为丢弃. 将正确的信息保存到ab文件中.
[root@kid ~]# ls /root /error >ab 2>/dev/null
[root@kid ~]# cat ab
/root:
ab
ab.txt
a.txt
b.txt
re.txt
# 将所有信息都存到黑洞设备中
[root@kid ~]# ls /root /error >ab &>/dev/null
# 什么都没有
[root@kid ~]# cat ab
2.7 脚本中使用重定向
判端口是否启用的脚本
# 编辑脚本
[root@kid ~]# vim ping.sh
ping -c1 10.0.0.1
if [ $? -eq 0 ];then
echo "10.0.0.1 is up."
else
echo "10.0.0.1 is down."
fi
[root@kid ~]# ll ping.sh
-rw-r--r--. 1 root root 117 Sep 2 22:31 ping.sh
# 为脚本添加可执行权限, 否则无法执行
[root@kid ~]# chmod +x ping.sh
# 启动脚本
[root@kid ~]# ./ping.sh
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms
# 网卡没有启动
10.0.0.1 is down.
# 改进后版 (将ping -c1 10.0.0.1 执行所有的信息丢进黑洞, 不需要)
[root@kid ~]# vim ping.sh
ping -c1 10.0.0.1 &>/dev/null
if [ $? -eq 0 ];then
echo "10.0.0.1 is up."
else
echo "10.0.0.1 is down."
fi
# 再次执行脚本(只要结果即可)
[root@kid ~]# ./ping.sh
10.0.0.1 is down.
2.8 脚本中使用重定向
[root@kid ~]# vim ping2.sh
# ping的执行结果丢进黑洞
ping -c1 10.0.0.1 &>/dev/null
if [ $? -eq 0 ];then
# ip地址启用信息追加到up.txt
echo "10.0.0.1 is up." >>up.txt
else
# ip地址启用信息追加到down.txt
echo "10.0.0.1 is down." >>down.txt
fi
# 为脚本添加可执行权限
[root@kid ~]# chmod +x ping2.sh
# 执行脚本
[root@kid ~]# ./ping2.sh
# 查看结果
[root@kid ~]# cat up.txt down.txt
cat: up.txt: No such file or directory
10.0.0.1 is down.
3. 输入重定向
输入重定向, 即原本从键盘等上获得的输入信息, 重定向由命令的输出作为输入.
3.1 读入
从文件中读入输入的操作
< 等价 0<
# 新建文件
[root@kid ~]# echo '123456' > num
# 将文件的信息作为输入
[root@kid ~]# grep '1' < num
123456
3.2 邮件案例
发送邮件, 需要先下载下载mailx: yum install mailx -y
# 没有改变输入的方向, 默认键盘
[root@kid ~]# mail qq
Subject: hello
123
456
. # 输入.结束
EOT
You have mail in /var/spool/mail/root
# 切换用户, 检查是否收到邮件
[root@kid ~]# su - q
[root@kid ~]# mail
[qq@kid ~]$ mail
Heirloom Mail version 12.5 7/5/10. Type ? for help.
"/var/spool/mail/qq": 1 message 1 new
>N 1 root Fri Sep 2 22:55 19/557 "hello"
& (输入1, 代表第一封邮件)
Message 1:
From root@kid.localdomain Fri Sep 2 22:55:44 2022
Return-Path: <root@kid.localdomain>
X-Original-To: qq
Delivered-To: qq@kid.localdomain
Date: Fri, 02 Sep 2022 22:55:44 +0800
To: qq@kid.localdomain
Subject: hello
User-Agent: Heirloom mailx 12.5 7/5/10
Content-Type: text/plain; charset=us-ascii
From: root@kid.localdomain (root)
Status: R
123
456
& (q 退出)
# 输入重定向, 来自于文件
[root@kid ~]# vi mail
abc
def
# -s 设置标题
[root@kid ~]# mail -s 'hello' qq < mail
# 切换用户
[root@kid ~]# su - qq
Last login: Sat Sep 3 17:57:08 CST 2022 on pts/0
# 查看邮件
[qq@kid ~]$ mail
Heirloom Mail version 12.5 7/5/10. Type ? for help.
"/var/spool/mail/qq": 2 messages 1 new
1 root Fri Sep 2 22:55 20/568 "hello"
>N 2 root Sat Sep 3 17:59 19/557 "hello"
& (输入2, 查看第二份邮件)
Message 2:
From root@kid.localdomain Sat Sep 3 17:59:44 2022
Return-Path: <root@kid.localdomain>
X-Original-To: qq
Delivered-To: qq@kid.localdomain
Date: Sat, 03 Sep 2022 17:59:44 +0800
To: qq@kid.localdomain
Subject: hello
User-Agent: Heirloom mailx 12.5 7/5/10
Content-Type: text/plain; charset=us-ascii
From: root@kid.localdomain (root)
Status: R
abc
def
& (q 退出)
3.3 产生文件
dd命令linux下功能强大的数据复制工具, 主要功能是拷贝文件, 默认从标准输入拷贝到标准输出.
常见用法:dd if=输入文件名 of=输出文件名
选项:
if=文件名:输入文件名,缺省为标准输入.即指定源文件.
of=文件名:输出文件名,缺省为标准输出.即指定目的文件.
bs=bytes:同时设置读入/输出的块大小为bytes个字节.
count=blocks:拷贝blocks块的个数, 块大小等于bs指定的字节数.
/dev/zero也是一个伪文件, 但它实际上产生连续不断的null的流(二进制的零流,而不是ASCII型的)
dev/zero 应用: 为特定的目的而用零去填充一个指定大小的文件.
# 生成一个一个20M的文件 file1.txt
[root@kid ~]# dd if=/dev/zero of=/file1.txt bs=1M count=20
20+0 records in
20+0 records out
20971520 bytes (21 MB) copied, 0.122722 s, 171 MB/s
# 查看文件大小
[root@kid ~]# ls -sh /file1.txt
20M /file1.txt
# 先创建file2.txt文件, 做为标准化输出的信息存放的地址, 再将从 /dev/zero获取数据
[root@kid ~]# dd </dev/zero >/file2.txt bs=1M count=20
3.4 mysql恢复备份
# 将mysql的配置文件作为标准输入
[root@kid ~]# mysql -uroot -p123 < bbs.sql
3.5 利用重定向建立文件
# 手动执行 shell 命令
[root@kid ~]# echo "111" > file1.txt
[root@kid ~]# cat file1.txt
111
# cat的标准输出由屏幕转到文本
[root@kid ~]# cat >file2.txt
111 (回车不在打印)
222
333
^D ( ctrl+ d 结束写入文件)
# 追加模式
[root@kid ~]# cat >> file3.txt
aaa
bbb
ccc
^D
3.6 cat脚本打印菜单
cat << -EOF(开始) '打印的信息' EOF(结束)
# 编辑脚本文件(复制不要整理这个格式了, 到vi中会显示很整齐)
[root@kid ~]# vim vm.sh
cat << EOF
+------------------------------+
| ============================ |
| 虚拟机基本管理 v5.0 by kid |
| ============================ |
| 1. 安装 KVM |
| 2. 安装或重置 CentOS-6.9 |
| 3. 安装或重置 CentOS-7.4 |
| 5. 安装或重置 Windows-7 |
| 6. 删除所有虚拟机 |
| q. 退出管理程序 |
+------------------------------+
EOF
* 注意最后一EOF独占一行, 一定不要出现多余的符号!!!
否则会报错:
./vm.sh: line 12: warning: here-document at line 1 delimited by
end-of-file (wanted `EOF')
vi/vim编辑器的命令行模式下: set invlist查看结尾符.

# 为文本添加可执行权限
chmod +x vm.sh vm.sh
# 查看菜单
[root@kid ~]# ./vm.sh
+------------------------------+
| ============================ |
| 虚拟机基本管理 v5.0 by kid |
| ============================ |
| 1. 安装 KVM |
| 2. 安装或重置 CentOS-6.9 |
| 3. 安装或重置 CentOS-7.4 |
| 5. 安装或重置 Windows-7 |
| 6. 删除所有虚拟机 |
| q. 退出管理程序 |
+------------------------------+
3.7 多条命令同时重定向
ps:
同时执行多条命令, 命令之间使用; 分号分隔即可.
Linux 终端命令的末尾加上一个 & 符号表示将这个任务放到后台去执行.
# 查询单前目录信息, 查看当前时间
[root@kid ~]# ls; date
file3.txt ll.sh ll.txt mail num vm.sh
Sat Sep 3 19:09:10 CST 2022
# date命令执行结果丢进黑洞
[root@kid ~]# ls; date &>/dev/null
file3.txt ll.sh ll.txt mail num vm.sh
# 两条命令的结果都放入黑洞中
[root@kid ~]# ls &>/dev/null; date &>/dev/null
# 代码简化
[root@kid ~]# (ls; date) &>/dev/null
# 编辑脚本(while循环5次, 间隔2秒打印一次)
[root@kid ~]# vi date.sh
#!/usr/bin/bash
num=0
while (( $num < 5 ))
do
date
(( num = $num + 1 ))
sleep 2
done
# 为脚本添加可执行权限
[root@kid ~]# chmod +x date.sh
# 执行脚本
[root@kid ~]# ./date.sh
Sat Sep 3 20:39:17 CST 2022
Sat Sep 3 20:39:19 CST 2022
Sat Sep 3 20:39:21 CST 2022
Sat Sep 3 20:39:23 CST 2022
Sat Sep 3 20:39:25 CST 2022
# 标准化输出到文本中
[root@kid ~]# ./1.sh &>date.txt &
[1] 1405
[root@kid ~]# tail date.txt
Sat Sep 3 20:42:25 CST 2022
Sat Sep 3 20:42:27 CST 2022
Sat Sep 3 20:42:29 CST 2022
Sat Sep 3 20:42:31 CST 2022
Sat Sep 3 20:42:33 CST 2022
Sat Sep 3 20:42:35 CST 2022
4. 进程管道技术
4.1 什么是管道
管道操作符号 “|” , 主要用来连接左右两个命令, 将左侧的命令的标准输出, 交给右侧命令的标准输入.
* 无法传递标准错误输出至后者命令
PS: 管道命令符能让大家能进一步掌握命令之间的搭配使用方法, 进一步提高命令输出值的处理效率.
4.2 管道流程示意图

格式: cmd1 | cmd2 [...|cmdn]
4.3 案例
案例1: 将/etc/passwd 中的用户按 UID 大小排序
# sort排序, -t指定分隔符, -k指定第三列 -n 按数值排序
[root@kid ~]# sort -t":" -k3 -n /etc/passwd
...
k1:x:1025:1036::/home/k1:/bin/bash
t1:x:1026:1037::/home/t1:/bin/bash
t2:x:1027:1038::/home/t2:/bin/bash
# -r 以相反的顺序来排序
[root@kid ~]# sort -t":" -k3 -n /etc/passwd -r
t2:x:1027:1038::/home/t2:/bin/bash
t1:x:1026:1037::/home/t1:/bin/bash
k1:x:1025:1036::/home/k1:/bin/bash
...
# 将排序后的信息交head命令处理
[root@kid ~]# sort -t":" -k3 -n /etc/passwd -r | head -3
t2:x:1027:1038::/home/t2:/bin/bash
t1:x:1026:1037::/home/t1:/bin/bash
k1:x:1025:1036::/home/k1:/bin/bash
案例2: 统计当前/etc/passwd 中用户使用的 shell 类型
# 思路:取出第七列(shell) | 排序(把相同归类)| 去重
# awk文本处理, -F知道分隔符 '{print $7}'的意思是选取并输出第7列的数据
[root@kid ~]# awk -F: '{print $7}' /etc/passwd
/bin/bash
/sbin/nologin
/sbin/nologin
...
# 排序不指定按字母顺序排
[root@kid ~]# awk -F: '{print $7}' /etc/passwd |sort
/bin/bash
/bin/sync
/sbin/halt
/sbin/nologin
...
[root@kid ~]# awk -F: '{print $7}' /etc/passwd |sort |uniq
[root@kid ~]# awk -F: '{print $7}' /etc/passwd |sort |uniq -c
案例3: 统计网站的访问情况 top 20 (跳过)
# 思路: 打印所有访问的连接 | 过滤访问网站的连接 | 打印用户的 IP | 排序 | 去重
[root@kid ~]# yum -y install httpd
# 重新启动httpd服务
[root@kid ~]# systemctl start httpd
# 光闭防火墙
[root@kid ~]# systemctl stop firewalld
[root@kid ~]# ss -an |grep :80 |awk -F":" '{print $8}' |sort |uniq -c
[root@kid ~]# ss -an |grep :80 |awk -F":" '{print $8}' |sort |uniq -c |sort -k1 -rn |head -n 20
案例4: 打印当前所有 IP
[root@kid ~]# ip addr |grep 'inet ' |awk '{print $2}' |awk -F"/" '{print $1}'
127.0.0.1
10.0.0.3
案例5: 打印根分区已用空间的百分比(仅打印数字)
[root@kid ~]# df |grep '/$' |awk '{print $5}' |awk -F"%" '{print $1}'
21
4.4 管道中的tee技术
tee指令会从标准输入设备读取数据, 将其内容输出到标准输出设备, 同时保存成文件.
选项:
-a 追加

[root@kid ~]# ip addr |grep 'inet ' |tee ip.txt |awk -F"/" '{print $1}' |awk '{print $2}'
127.0.0.1
10.0.0.3
# ip addr |grep 'inet '过滤的信息
[root@kid ~]# cat ip.txt
inet 127.0.0.1/8 scope host lo
inet 10.0.0.3/24 brd 10.0.0.255 scope global noprefixroute ens32
重定向与 tee 使用过程中有什么区别
# 直接将标准化输入内容写入date.txt文件中
[root@kid ~]# date > date.txt
# 命令执行结果会输出至屏幕, 但会同时保存一份至date.txt文件中
[root@kid ~]# date |tee date.txt
4.5 xargs参数传递
xargs参数传递, 主要让一些不支持管道的命令可以使用管道技术.
* 不支持重命名的命令, 需要使用原生命令(例子, ll =的原生命令=> ls -l ).
选项:
-i -i时以大括号{}作为替换符号, 传递的时候看到{}就将被结果替换。
可以将{}放在任意需要传递的参数位上,如果多个地方使用{}就实现了多个传递。
-I 和xargs -i是一样的, 只是-i默认使用大括号作为替换符号,
-I则可以指定其他的符号、字母、数字作为替换符号, 但是必须用引号包起来.
# which命令的作用是, 在PATH变量指定的路径中, 搜索某个系统命令的位置.
[root@kid ~]# which cat
/usr/bin/cat
# 查看当前目录的信息
[root@kid ~]# ll
total 4
-rw-r--r--. 1 root root 0 Sep 3 21:07 1.txt
-rw-r--r--. 1 root root 104 Sep 3 21:50 ip.txt
# 我们期望是将which cat的结果交给ls处理, 可并实现...
[root@kid ~]# which cat | ls -l
# 还是root目录
total 4
-rw-r--r--. 1 root root 0 Sep 3 21:07 1.txt
-rw-r--r--. 1 root root 104 Sep 3 21:50 ip.txt
# 想要的效果
[root@kid ~]# which cat|xargs ls -l
-rwxr-xr-x. 1 root root 54080 Aug 20 2019 /usr/bin/cat
# 不能使用重命名的命令
[root@kid ~]# which cat|xargs ll
xargs: ll: No such file or directory
# ls可查看当前目录下的非隐藏文件信息, 将当前目录所有非隐藏文件目录删除
[root@kid ~]# ls |xargs rm -fv
removed ‘1.txt’
removed ‘ip.txt
# 新建文件
[root@kid ~]# touch 1.txt
[root@kid ~]# ls
1.txt
# 将当前目录下的文件复制到 /tmp/目录下
[root@kid ~]# ls |xargs cp -rvt /tmp/
‘1.txt’ -> ‘/tmp/1.txt’
# 将当前目录下的文件启动到 /tmp/目录下
[root@kid ~]# ls |xargs mv -t /tmp/
# 生成文件名.backup备份文件 (ls 将一个个展示信息作为参数传递给{}, {}相当于变量)
[root@kid ~]# touch {a..c}.txt
# 第一次将a.txt传给第一个{}, 第一个{}被使用并添加后缀为a.txt.backup
[root@kid ~]# ls | xargs -i mv {} {}.backup
[root@kid ~]# ls
a.txt.backup b.txt.backup c.txt.backup
# 使用-I 需要先指定替换符在使用
[root@kid ~]# touch {a..c}.txt
[root@kid ~]# ls | xargs -I 'xx' mv xx /tmp
[root@kid ~]# ls /tmp
a.txt b.txt c.txt
# xargs -i 分批接收参数(一行为一批)
[root@kid ~]# vi 1.txt
123
456
789
[root@kid ~]# cat 1.txt | xargs -i echo "开始 {} 结束"
开始 123 结束
开始 456 结束
开始 789 结束
————————————————
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
文章的段落全是代码块包裹的, 留言是为了避免文章提示质量低.
————————————————
2858

被折叠的 条评论
为什么被折叠?



