目录
3.1.2 示例:查找修改时间在7天之前或文件名以.bak结尾的文件
Shell 脚本常见语法错误
Shell 脚本中的语法错误多种多样,以下是常见的错误类型及示例,帮助您快速识别和修复问题:
一、命令结构错误
1. 缺失关键字
# 错误:缺少 for 关键字 ((i=1; i<=5; i++)) # 应改为 for ((i=1; i<=5; i++)) # 错误:缺少 do/done if [ 1 -eq 1 ] echo "true" # 应改为 do echo "true"; done
2. 括号不匹配
# 错误:括号未闭合 for ((i=1; i<=5; i++ # 缺少右括号 )) # 错误:if 语句括号不匹配 if [ $num -gt 10 # 缺少右括号 ]
3. 错误的分隔符
# 错误:for 循环中错误使用分号 for i in 1,2,3 # 应使用空格:for i in 1 2 3
二、变量相关错误
1. 未定义变量
# 错误:引用未定义变量 echo $name # 未定义 name 变量 # 正确:先定义后使用 name="Shell" echo $name
2. 变量赋值错误
# 错误:赋值时等号周围有空格 name = "Shell" # 应改为 name="Shell" # 错误:使用 $ 符号赋值 $name="Shell" # 应改为 name="Shell"
3. 变量展开错误
# 错误:未使用 ${} 包裹变量
echo "Hello $name1" # 变量被解析为 name1,而非 $name+1
# 正确:明确变量边界
echo "Hello ${name}1"
三、引号与转义错误
1. 引号不匹配
# 错误:单引号未闭合 echo 'Hello # 缺少右单引号' # 错误:双引号嵌套未转义 echo "She said 'Hello'" # 正确:echo 'She said "Hello"'
2. 特殊字符未转义
# 错误:命令替换未使用 $() echo "当前目录: `pwd`" # 推荐:echo "当前目录: $(pwd)" # 错误:通配符未转义 echo "查找文件: *.txt" # 应转义:echo "查找文件: \*.txt"
四、条件判断错误
1. 测试命令语法错误
# 错误:[ 与 ] 之间缺少空格 if [1 -eq 1]; then # 应改为 if [ 1 -eq 1 ]; then # 错误:错误的比较操作符 if [ $num = 10 ] # 数值比较应使用 -eq:if [ $num -eq 10 ]
2. 文件测试错误
# 错误:测试文件是否存在 if [ -f $file ] # 未考虑 $file 为空的情况 # 正确:先检查变量是否为空 if [ -n "$file" ] && [ -f "$file" ]; then
五、循环结构错误
1. for 循环语法错误
# 错误:C风格循环缺少 for 关键字 ((i=1; i<=5; i++)) # 应改为 for ((i=1; i<=5; i++)) # 错误:列表循环未使用 in 关键字 for i 1 2 3 # 应改为 for i in 1 2 3
2. while 循环错误
# 错误:条件永远为真 while true # 缺少 do/done echo "循环" # 正确: while true; do echo "循环" done
六、其他常见错误
1. Shebang 错误
# 错误:Shebang 路径错误 #!/bin/bash # 路径不存在或错误 # 正确:使用系统实际路径 #!/usr/bin/env bash
2. 命令拼写错误
# 错误:命令拼写错误 ech "Hello" # 应改为 echo "Hello" # 错误:命令不存在 mycommand # 未安装或路径未添加到 $PATH
3. 重定向错误
# 错误:重定向符号错误(追加和覆盖的区别) echo "Hello" > file.txt # 正确:echo "Hello" >> file.txt # 错误:文件权限不足 echo "Hello" > /etc/config.txt # 无写入权限
调试技巧
-
使用
set -x:显示命令执行过程,帮助定位错误。 -
启用严格模式:在脚本开头添加
set -euxo pipefail,强制检查错误。 -
逐行测试:将复杂脚本拆分为小片段,逐步验证。
-
使用 ShellCheck:在线工具(ShellCheck – shell script analysis tool)自动检查语法错误。
脚本调试:
sh 命令调试选项(推荐)
选项 说明 -c 从-c后的字符串中读取命令。 -n 检查是否存在语法错误,但不会实际执行。 -x 将执行的每一条命令和结果依次打印出来。 -v 执行过的脚本命令打印到标准输出。
使用方法:
字符串读取脚本。
$ sh -c 'if [ 1 -lt 2 ];then echo "true"; else echo "false"; fi' true
注:临时测试 shell 语法或者小段脚本时使用。
检查脚本是否存在语法错误。
$ sh -n daodaotest.sh
跟踪调试 shell 脚本,将执行的每一条命令结果依次打印出来。
-
$ sh -x daodaotest.sh + '[' 2 -lt 2 ']' + COUNT=3 + PARAMETER=daodaotest + (( i = 1 )) + (( i <= 3 )) + echo '第 1 遍打印:daodaotest' 第 1 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) + echo '第 2 遍打印:daodaotest' 第 2 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) + echo '第 3 遍打印:daodaotest' 第 3 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) + exit 0
跟踪调试 shell 脚本,将执行的每一条命令和结果依次打印出来。
$ sh -xv daodaotest.sh #!/bin/bash
调试脚本示例
使用方法
usage() {
echo "Usage: sh $0 COUNT PARAMETER"
echo "\t COUNT 循环打印次数"
echo "\t PARAMETER 打印字符串"
echo "示例:"
echo "\t 1. 打印 daodaotest 2 次"
echo "\t sh $0 2 daodaotest"
}
判断参数
-
if [ $# -lt 2 ]; then usage exit 1 fi + '[' 2 -lt 2 ']'
打印次数
-
COUNT=$1 + COUNT=3
打印字符串
-
PARAMETER=$2 + PARAMETER=daodaotest
循环打印
-
for (( i = 1; i <= $COUNT; i++)); do echo "第 $i 遍打印:$PARAMETER" done + (( i = 1 )) + (( i <= 3 )) + echo '第 1 遍打印:daodaotest' 第 1 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) + echo '第 2 遍打印:daodaotest' 第 2 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) + echo '第 3 遍打印:daodaotest' 第 3 遍打印:daodaotest + (( i++ )) + (( i <= 3 )) exit 0 + exit 0
注:本人最常用-x参数,能解决 90% 的脚本调试问题。
set
主要用于:
-
控制 Shell 的行为选项(如错误处理、调试)。
-
操作位置参数。
-
查看环境变量和函数。
一、错误处理选项
set -e # 遇到错误(非零退出状态)时立即退出脚本 set -u # 引用未定义变量时抛出错误 set -o pipefail # 管道中任何命令失败,整个管道返回非零状态
组合使用(推荐在脚本开头添加):
#!/bin/bash set -euo pipefail
-
调试选项
set -x # 执行命令前打印命令及其参数(跟踪执行过程) set +x # 关闭调试模式
示例:
set -x ls /tmp echo "Done" set +x
输出会显示执行的命令及参数:
+ ls /tmp file1.txt file2.txt + echo "Done" Done
二、定义位置参数
set 可以重新定义脚本或函数的位置参数($1, $2, ...):
set -- "参数1" "参数2" "参数3" # -- 表示参数列表开始 echo $1 # 输出:参数1 echo $2 # 输出:参数2
应用场景:在脚本中动态修改参数,例如:
#!/bin/bash set -- "$@" "--verbose" # 给原有参数添加 --verbose echo "所有参数: $*" set --:用于重新设置位置参数(即 $1, $2, $3 等) "$@":以数组形式引用所有原始参数,并且会保留参数中的空格 "--verbose":在原有参数列表的末尾添加 --verbose 选项,-v是其缩写
执行:
./script.sh a b c # 输出:所有参数: a b c --verbose
三、显示所有环境变量和函数
set # 不带任何参数时,显示当前所有环境变量和函数定义
输出类似:
BASH=/bin/bash
HOME=/home/user
PATH=/usr/bin:/bin:/usr/sbin:/sbin(多个路径间用:隔开)
...
hello() {
echo "Hello, world!"
}
解释
hello() 是一个 Bash函数,用于封装可复用的代码块:
1. 语法结构
函数名() {
命令序列
}
例:
hello() {
echo "Hello, world!"
}
# 调用函数
hello # 输出:Hello, world!
四、常用选项速查表
| 选项 | 作用 | 反向选项 |
|---|---|---|
-e | 出错时立即退出 | +e |
-u | 未定义变量报错 | +u |
-x | 打印执行的命令(调试模式) | +x |
-o pipefail | 管道中任一命令失败则整个管道失败 | +o pipefail |
-f | 禁用文件名通配符(如 *, ?) | +f |
-v | 打印读取的命令行(类似 -x,但更早) | +v |
五、注意事项
-
临时修改选项:
# 临时启用调试模式 (set -x; ls /tmp) # 括号创建子shell,只在子shell中生效 例: $ (set -x; ls /tmp) + ls /tmp file1.txt dir1 分号用于隔开命令
-
与
unset的区别:-
set用于设置变量或选项。 -
unset:用于删除变量或函数,例如:
unset VAR # 删除变量 VAR
-
-
脚本中的最佳实践:
-
在脚本开头使用
set -euo pipefail提高健壮性。 -
使用 trap命令捕获错误并清理资源,例如:
trap 'echo "Error occurred"; exit 1' ERR
-
六、示例:使用 set 编写健壮的脚本
#!/bin/bash set -euo pipefail # 定义位置参数 set -- "file1.txt" "file2.txt" # 处理每个文件 for file in "$@"; do if [[ -f "$file" ]]; then echo "处理文件: $file" # 执行操作... else echo "错误: 文件 $file 不存在" >&2 exit 1 fi done
七、基本用法
基本用法与示例
# 格式:sed [选项] '命令' 输入文件
-
替换文本(
s命令)# 将文件中的"hello"替换为"hi" sed 's/hello/hi/g' input.txt # 忽略大小写替换 sed 's/hello/hi/gi' input.txt
-
删除行(
d命令)# 删除包含"error"的行 sed '/error/d' log.txt # 删除第3行 sed '3d' file.txt
-
插入与追加(
i、a命令)# 在包含"start"的行前插入新行 sed '/start/i\新插入的内容' file.txt # 在文件末尾追加内容 sed '$a\追加到最后一行' file.txt
-
修改文件(
-i选项)# 直接修改文件(添加备份后缀) sed -i '.bak' 's/old/new/g' file.txt
1、find基本语法
find命令的基本语法如下:
find [path...] [expression]
其中,path是查找的起始路径,可以是目录名、文件名或通配符。expression是用于指定搜索条件的表达式,它可以包含多个选项和操作符。
2、常用选项
| 选项 | 作用 |
|---|---|
| -amin<分钟> | 查找在指定时间曾被存取过的文件或目录,单位以分钟计算; |
| - anewer<参考文件或目录> | 查找其存取时间较指定文件或目录的存取时间更接近现在的文件或目录; |
| - atime<24小时数> | 查找在指定时间曾被存取过的文件或目录,单位以24小时计算; |
| - cmin<分钟> | 查找在指定时间之时被更改过的文件或目录; |
| - cnewer<参考文件或目录> | 查找其更改时间较指定文件或目录的更改时间更接近现在的文件或目录; |
| - ctime<24小时数> | 查找在指定时间之时被更改的文件或目录,单位以24小时计算; |
| - daystart | 从本日开始计算时间; |
| - depth | 从指定目录下最深层的子目录开始查找; |
| - expty | 寻找文件大小为0 Byte的文件,或目录下没有任何子目录或文件的空目录; |
| - exec<执行指令> | 假设find指令的回传值为True,就执行该指令; |
| - false | 将find指令的回传值皆设为False; |
| - fls<列表文件> | 此参数的效果和指定“ - ls”参数类似,但会把结果保存为指定的列表文件; |
| - follow | 排除符号连接; |
| - fprint<列表文件> | 此参数的效果和指定“ - print”参数类似,但会把结果保存成指定的列表文件; |
| - fprint0<列表文件> | 此参数的效果和指定“ - print0”参数类似,但会把结果保存成指定的列表文件; |
| - fprintf<列表文件><输出格式> | 此参数的效果和指定“ - printf”参数类似,但会把结果保存成指定的列表文件; |
| - fstype<文件系统类型> | 只寻找该文件系统类型下的文件或目录; |
| - gid<群组识别码> | 查找符合指定之群组识别码的文件或目录; |
| - group<群组名称> | 查找符合指定之群组名称的文件或目录; |
| - help或——help | 在线帮助; |
| - ilname<范本样式> | 此参数的效果和指定“ - lname”参数类似,但忽略字符大小写的差别; |
| - iname<范本样式> | 此参数的效果和指定“ - name”参数类似,但忽略字符大小写的差别; |
| - inum<inode编号> | 查找符合指定的inode编号的文件或目录; |
| - ipath<范本样式> | 此参数的效果和指定“ - path”参数类似,但忽略字符大小写的差别; |
| - iregex<范本样式> | 此参数的效果和指定“ - regexe”参数类似,但忽略字符大小写的差别; |
| - links<连接数目> | 查找符合指定的硬连接数目的文件或目录; |
| - iname<范本样式> | 指定字符串作为寻找符号连接的范本样式; |
| - ls | 假设find指令的回传值为Ture,就将文件或目录名称列出到标准输出; |
| - maxdepth<目录层级> | 设置最大目录层级; |
| - mindepth<目录层级> | 设置最小目录层级; |
| - mmin<分钟> | 查找在指定时间曾被更改过的文件或目录,单位以分钟计算; |
| - mount | 此参数的效果和指定“ - xdev”相同; |
| - mtime<24小时数> | 查找在指定时间曾被更改过的文件或目录,单位以24小时计算; |
| - name<范本样式> | 指定字符串作为寻找文件或目录的范本样式; |
| - newer<参考文件或目录> | 查找其更改时间较指定文件或目录的更改时间更接近现在的文件或目录; |
| - nogroup | 找出不属于本地主机群组识别码的文件或目录; |
| - noleaf | 不去考虑目录至少需拥有两个硬连接存在; |
| - nouser | 找出不属于本地主机用户识别码的文件或目录; |
| - ok<执行指令> | 此参数的效果和指定“ - exec”类似,但在执行指令之前会先询问用户,若回答“y”或“Y”,则放弃执行命令; |
| - path<范本样式> | 指定字符串作为寻找目录的范本样式; |
| - perm<权限数值> | 查找符合指定的权限数值的文件或目录; |
| 假设find指令的回传值为Ture,就将文件或目录名称列出到标准输出。格式为每列一个名称,每个名称前皆有“. / ”字符串; | |
| - print0 | 假设find指令的回传值为Ture,就将文件或目录名称列出到标准输出。格式为全部的名称皆在同一行; |
| - printf<输出格式> | 假设find指令的回传值为Ture,就将文件或目录名称列出到标准输出。格式可以自行指定; |
| - prune | 不寻找字符串作为寻找文件或目录的范本样式; |
| -regex<范本样式> | 指定字符串作为寻找文件或目录的范本样式; |
| - size<文件大小> | 查找符合指定的文件大小的文件; |
| - true | 将find指令的回传值皆设为True; |
| - type<文件类型> | 只寻找符合指定的文件类型的文件; |
| - uid<用户识别码> | 查找符合指定的用户识别码的文件或目录; |
| - used<日数> | 查找文件或目录被更改之后在指定时间曾被存取过的文件或目录,单位以日计算; |
| - user<拥有者名称> | 查找符和指定的拥有者名称的文件或目录; |
| - version或——version | 显示版本信息; |
| - xdev | 将范围局限在先行的文件系统中; |
| - xtype<文件类型> | 此参数的效果和指定“ - type”参数类似,差别在于它针对符号连接检查。 |
2.1 示例:查找大于1MB的文件
find /path/to/search -type f -size +1M
上述命令将在指定路径下查找所有大小大于1MB的文件。
2.2 示例:查找最近7天内修改过的文件
find /path/to/search -mtime -7
这个例子将在指定路径下查找最近7天内修改过的文件。
2.3 示例:查找属主为user1的文件
find /path/to/search -user user1
上述命令将在指定路径下查找所有属主为user1的文件。
2.4 示例:执行自定义命令
find /path/to/search -name "*.log" -exec rm {} \;
这个例子将删除指定路径下所有扩展名为.log的文件。
3、高级用法
3.1 使用逻辑操作符
find命令支持逻辑操作符,如-a(与)、-o(或)、!(非),用于组合多个条件。
3.1.1 示例:查找大于1MB且是普通文件的文件
find /path/to/search -type f -size +1M -a -type f
上述命令将查找指定路径下所有大小大于1MB且是普通文件的文件。
3.1.2 示例:查找修改时间在7天之前或文件名以.bak结尾的文件
find /path/to/search \( -mtime +7 -o -name "*.bak" \)
这个例子将查找指定路径下修改时间在7天之前或文件名以.bak结尾的文件。
3.2 搜索多个路径
3.2.1 示例:查找多个目录下的所有文件
find /path/to/dir1 /path/to/dir2 -type f
上述命令将在dir1和dir2两个目录中查找所有文件。
3.3 将find结果用于其他命令
3.3.1 示例:在搜索结果中执行grep
find /path/to/search -type f -name "*.txt" -exec grep "pattern" {} \;
上述命令将在指定路径下所有扩展名为.txt的文件中搜索包含指定模式的行。
3.3.2 示例:将搜索结果输出到文件
find /path/to/search -type f -name "*.log" > logfiles.txt
这个例子将查找指定路径下所有扩展名为.log的文件,并将结果输出到名为logfiles.txt的文件中。
4、find命令的性能优化
在处理大量文件时,find命令的性能可能成为一个考虑因素。以下是一些建议用于提高性能的技巧:
-
尽量减少-exec选项的使用,因为每个匹配的文件都会执行一次命令。
-
使用-print选项代替-exec,将结果输出到标准输出,然后使用管道将结果传递给其他命令。
-
使用-maxdepth选项限制递归的深度,以减少搜索的范围。
bc命令
bc命令 是一种支持任意精度的交互执行的计算器语言。bash内置了对整数四则运算的支持,但是并不支持浮点运算,而bc命令可以很方便的进行浮点运算,当然整数运算也不再话下。
1、语法
bc [选项][参数]
2、选项
-i:强制进入交互式模式; -l:定义使用的标准数学库; -w:对POSIX bc的扩展给出警告信息; -q:不打印正常的GNU bc环境信息; -v:显示指令版本信息; -h:显示指令的帮助信息。
3、参数
文件:指定包含计算任务的文件。
4、实例
算术操作高级运算bc命令它可以执行浮点运算和一些高级函数:
echo "1.212*3" | bc 3.636
设定小数精度(数值范围)
echo "scale=2;3/8" | bc 0.37
参数scale=2是将bc输出结果的小数位设置为2位。
进制转换
#!/bin/bash abc=192 echo "obase=2;$abc" | bc
执行结果为:11000000,这是用bc将十进制转换成二进制。
#!/bin/bash abc=11000000 echo "obase=10;ibase=2;$abc" | bc
执行结果为:192,这是用bc将二进制转换为十进制。
计算平方和平方根:
echo "10^10" | bc echo "sqrt(100)" | bc
awk命令
awk是一种功能强大的文本处理语言,特别擅长按列切割和处理结构化数据(如 CSV、日志等)。与cut相比,awk支持更复杂的模式匹配、条件判断和计算。
基本语法
awk '模式 {动作}' 文件
-
模式:可选,用于筛选行(如
/pattern/或条件表达式)。 -
动作:对匹配行执行的操作(如打印、计算)。
核心概念
-
内置变量:
-
$0:整行内容。 -
$1, $2, ...:第 1 列、第 2 列... -
NF:当前行的字段数。 -
FS:字段分隔符(默认空格或制表符)。
-
-
分隔符设置:
awk -F',' '{动作}' # 按逗号分隔 awk -v FS='\t' '{动作}' # 按制表符分隔
示例:切割文件
1. 按空格切割,提取第 1 列和第 3 列
# 文件内容:name age city
# 输出:name city
awk '{print $1, $3}' data.txt
2. 按逗号切割 CSV 文件
# 文件内容:Alice,25,New York
# 输出:Alice New York
awk -F',' '{print $1, $3}' data.csv
3. 按自定义分隔符切割
# 按冒号分隔
awk -F':' '{print $1, $NF}' config.ini # 打印第1列和最后一列
高级应用
1. 条件筛选
# 只打印第2列大于20的行
awk -F',' '$2 > 20 {print $1, $2}' data.csv
# 只打印包含"ERROR"的行
awk '/ERROR/ {print $0}' log.txt
2. 计算与统计
# 计算第2列的平均值
awk -F',' '{sum += $2} END {print sum/NR}' data.csv
# 统计每个城市的人数
awk -F',' '{city[$3]++} END {for (c in city) print c, city[c]}' data.csv
3. 格式化输出
# 按列对齐输出
awk -F',' '{printf "%-10s %-5s %-15s\n", $1, $2, $3}' data.csv
与cut的对比
| 功能 | cut | awk |
|---|---|---|
| 字段分隔符 | 单一字符(如,) | 支持正则(如[:space:]) |
| 条件筛选 | 不支持 | 支持复杂条件(如$2>20) |
| 计算与统计 | 不支持 | 支持数学运算、数组 |
| 输出格式化 | 简单连接 | 支持printf格式化 |
总结
-
awk适合:复杂的字段提取、条件筛选、数据统计。 -
cut适合:简单的按列切割,语法更简洁。
pidof命令
pidof 命令详解:快速查找进程 ID
pidof 是 Linux 系统中用于查找运行中进程的进程 ID(PID)的实用工具。它通过进程名称快速定位对应的 PID,常用于脚本自动化、进程监控和管理。
一、基本语法与功能
pidof [选项] 进程名
-
功能:返回与指定进程名匹配的所有进程的 PID(以空格分隔)。
-
示例:查找名为nginx的进程 ID:
$ pidof nginx 1234 5678 9012 # 可能返回多个 PID(如主进程和工作进程)
二、常用选项
| 选项 | 功能描述 |
|---|---|
-s | 仅返回一个 PID(即使有多个匹配进程,通常返回第一个)。 |
-c | 仅返回运行在当前目录的进程(对守护进程无效)。 |
-x | 同时匹配脚本名(如 .sh 文件)。 |
-o PID | 排除指定 PID 的进程(常用于排除当前脚本自身)。 |
-o PPID | 排除指定父进程 ID(PPID)的进程。 |
-q | 静默模式(不输出任何信息,仅通过返回值判断进程是否存在)。 |
-h 或 -? | 显示帮助信息。 |
三、常见使用场景
1. 查找单个进程的 PID
# 查找 SSH 服务进程 $ pidof sshd 2345 # 返回 SSH 守护进程的 PID # 仅返回一个 PID $ pidof -s sshd 2345
2. 结合其他命令使用
-
终止进程(配合
kill):# 杀死所有 nginx 进程 kill -9 $(pidof nginx)
-
监控进程状态(配合
ps):# 查看 nginx 进程的详细信息 ps -fp $(pidof nginx)
3. 脚本中检查进程是否存在
if pidof -q nginx; then
echo "Nginx is running."
else
echo "Nginx is not running."
fi
4. 排除自身进程
当脚本需要查找同名进程时,排除自身:
# 查找除当前脚本外的所有 bash 进程 pidof -o $$ bash # $$ 表示当前脚本的 PID
四、与其他命令的对比
| 命令 | 特点 | 适用场景 |
|---|---|---|
pidof | 直接通过进程名查找 PID,简单快速。 | 脚本自动化、快速定位进程。 |
pgrep | 功能更强大,支持正则表达式、用户、UID 等多种过滤条件。 | 复杂查询需求。 |
ps aux | grep | 需要管道和文本处理,灵活性高但效率较低。 | 人工交互、模糊匹配。 |
systemctl status | 针对 systemd 管理的服务,返回服务状态和 PID。 | 管理 systemd 服务。 |
五、常见问题与注意事项
-
进程名匹配规则:
-
pidof匹配的是进程的 实际名称(即ps命令显示的CMD列),而非完整路径。 -
例如,
/usr/bin/nginx的进程名是nginx。
-
-
同名进程处理:
-
若存在多个同名进程,
pidof默认返回所有 PID,需用-s限制。
-
-
脚本兼容性:
-
部分系统(如 macOS)可能没有
pidof,需使用pgrep替代:
# macOS 等效命令 pgrep nginx
-
-
权限限制:
-
普通用户只能查看自己的进程,root 用户可查看所有进程。
-
六、总结
pidof 是快速获取进程 ID 的实用工具,适合在脚本中自动化操作。掌握其基本用法和选项后,可高效完成进程监控、管理和状态检查等任务。若需更复杂的查询,建议使用 pgrep 命令。
33万+

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



