Bash Shell脚本学习——唇读数据集格式修复脚本
文章目录
- 【1】脚本目的
- 【2】脚本内容
- 【3】逐句语法分析
- (1)`find "$DATA_PATH" -name "\*.pkl" -type f | head -20`
- (2)`find "$DATA_PATH" -name "\*.pkl" -type f | wc -l`
- (3)`head -1 label_sorted.txt | od -c | head -3`
- (4)`head -1 label_sorted.txt | grep -P '\r' && echo "发现\\r字符"`
- (5)`head -1 label_sorted.txt | grep -P '\r' && echo "发现\\r字符"`
- (6)`cp label_sorted.txt label_sorted.txt.bak`
- (7)`cat label_sorted.txt | tr -d '\r' > label_sorted.txt.new`
- (8)`mv label_sorted.txt.new label_sorted.txt`
- (9)`while read -r label; do`
- (10)`TOTAL=$((TOTAL + 1))`
- (11)`[ "$TRAIN" -gt 0 ] && [ "$VAL" -gt 0 ] && WITH_DATA=$((WITH_DATA + 1))`
- 【4】脚本中的知识点总结
【1】脚本目的
在部署唇读识别项目时,我遇到了一个奇怪的现象:数据集明明完整存在,但训练脚本却报告所有标签的train和val数据都是0!经过系统排查,最终发现罪魁祸首竟然是——Windows换行符。
【2】脚本内容
#(2)确认数据文件存在
# 查找所有pkl文件
find "$DATA_PATH" -name "*.pkl" -type f | head -20 # 前20个pkl文件
find "$DATA_PATH" -name "*.pkl" -type f | wc -l # pkl文件总数#(3)检查标签格式
# 检查第一个标签的格式
head -1 label_sorted.txt | od -c | head -3 # 查看不可见字符
head -1 label_sorted.txt | grep -P '\r' && echo "发现\\r字符" # 检查Windows换行符#(4)修复步骤
cp label_sorted.txt label_sorted.txt.bak # 备份
cat label_sorted.txt | tr -d '\r' > label_sorted.txt.new
mv label_sorted.txt.new label_sorted.txt # 替换原文件
echo "验证修复后的数据情况:"
TOTAL=0
WITH_DATA=0while read -r label; do[ -z "$label" ] && continueTOTAL=$((TOTAL + 1))TRAIN=$(find "$DATA_PATH/$label/train" -name "*.pkl" 2>/dev/null | wc -l)VAL=$(find "$DATA_PATH/$label/val" -name "*.pkl" 2>/dev/null | wc -l)[ "$TRAIN" -gt 0 ] && [ "$VAL" -gt 0 ] && WITH_DATA=$((WITH_DATA + 1))
done < label_sorted.txtecho "标签总数: $TOTAL"
echo "有完整数据的标签: $WITH_DATA"
【3】逐句语法分析
(1)find "$DATA_PATH" -name "\*.pkl" -type f | head -20
-
命令: 文件查找与结果限制。
-
语法:
-
DATA_PATH="/home/liyana/my dataset/"引号告诉Shell:
"/home/liyana/my dataset/"是一个完整的字符串;变量DATA_PATH存储的值是:/home/liyana/my dataset/(不包含引号) -
find 路径: 在指定路径下递归搜索文件和目录。 -
-name "模式": 按文件名匹配,*为通配符代表任意字符。 -
-type f: 只搜索普通文件(file),排除目录等其他类型。 -
| head -20: 将查找结果通过管道传递给head,仅显示前20行。
-
(2)find "$DATA_PATH" -name "\*.pkl" -type f | wc -l
- 命令: 文件查找与数量统计。
- 语法:
find "$DATA_PATH" -name "*.pkl" -type f: 查找所有pkl格式的普通文件。| wc -l: 将找到的文件列表通过管道传递给wc -l统计行数,即文件总数。
(3)head -1 label_sorted.txt | od -c | head -3
- 命令: 以ASCII字符形式显示文件内容,可以看到空格、换行符、制表符等不可见字符。
- 语法:
head -1 label_sorted.txt: 提取标签文件的第1行。| od -c: 通过管道将内容传递给od(octal dump) 命令,-c参数表示用字符形式显示。| head -3: 仅显示od命令输出的前3行,避免信息过多。
(4)head -1 label_sorted.txt | grep -P '\r' && echo "发现\\r字符"
-
命令: 专门检查文件中是否包含Windows换行符 (
\r\n),这在Linux环境下可能导致解析问题。 -
语法:
-
head -1 label_sorted.txt: 同样提取第1行。 -
| grep -P '\r': 通过管道用grep(Global Regular Expression Print)搜索,-P启用Perl正则表达式,\r匹配回车符。 -
grep -P:超级模式,支持各种高级匹配语法 含义 用途 \r回车符 检测Windows换行 \n换行符 检测Linux换行 \t制表符 找到tab字符 \x{263a}Unicode字符 匹配表情符号等 \d数字 等同于 [0-9]\s空白字符 空格、tab、换行等 -
&& echo "发现\\r字符": 如果前面的grep命令成功找到\r字符,则执行echo输出提示信息。
-
(5)head -1 label_sorted.txt | grep -P '\r' && echo "发现\\r字符"
- 命令: 特定字符检测与条件提示。
- 语法:
head -1 label_sorted.txt: 同样提取第1行。| grep -P '\r': 通过管道用grep搜索,-P启用Perl正则表达式,\r匹配回车符。&& echo "发现\\r字符": 如果前面的grep命令成功找到\r字符,则执行echo输出提示信息。
- 作用: 专门检查文件中是否包含Windows换行符 (
\r\n),这在Linux环境下可能导致解析问题。
(6)cp label_sorted.txt label_sorted.txt.bak
- 命令: 文件备份。
- 语法:
cp: copy ,复制文件或目录。cp 源文件 目标文件: 复制源文件到目标文件。- 如果目标文件已存在,则会被覆盖。
(7)cat label_sorted.txt | tr -d '\r' > label_sorted.txt.new
- 命令: 读取原标签文件,删除其中所有的Windows回车符,并将"净化"后的内容保存到一个新文件中。
- 语法:
cat label_sorted.txt: catenate( 串联),读取并输出指定文件的内容。(cat file1.txt file2.txt file3.txt把三个文件串联起来显示)|: 管道,将前一个命令的输出作为后一个命令的输入。tr -d '\r': translate(字符转换/转译),-d参数表示 delete,删除输入流中所有的\r(回车符) 字符。> label_sorted.txt.new: 输出重定向,将前面管道处理后的结果写入到新文件label_sorted.txt.new(会覆盖已存在的文件)。
(8)mv label_sorted.txt.new label_sorted.txt
- 命令: 用已修复的新文件替换原始文件,完成修复操作。
- 语法:
mv 源文件 目标文件: move,将源文件移动至目标文件。
(9)while read -r label; do
while read -r label; do# 循环体...
done < label_sorted.txt # 👈 输入来自这个文件!
- 命令: 开始一个循环,逐行读取文件内容,每行内容存入变量
label中。 - 语法:
while ...; do ... done:while循环结构,当条件为真时,重复执行do和done之间的代码块。read -r label:read命令从标准输入读取一行,-r参数表示 raw (原始模式,不解释反斜杠转义符),并将读取的内容赋值给变量label。
(10)TOTAL=$((TOTAL + 1))
- 命令: 将变量
TOTAL的值加1,实现标签总数的计数。 - 语法:
$(( 算术表达式 )): 算术扩展,Shell会计算双括号内表达式的值。- 变量无需在表达式内加
$符号引用。常见用法:result=$((a + b))
(11)[ "$TRAIN" -gt 0 ] && [ "$VAL" -gt 0 ] && WITH_DATA=$((WITH_DATA + 1))
-
命令: 如果当前标签的训练集和验证集文件数量都大于0,则将有效数据计数器
WITH_DATA加1。 -
语法:
-
[ "$TRAIN" -gt 0 ]: 条件测试,判断变量TRAIN的值是否 greater than (大于) 0。 -
当变量包含空格、制表符时,没有引号"$TRAIN"会出问题:
# 假设 TRAIN 的值是 "5"(正常情况) [ $TRAIN -gt 0 ] # 替换后:[ 5 -gt 0 ] → ✅ 正常# 假设 TRAIN 的值是 ""(空字符串) [ $TRAIN -gt 0 ] # 替换后:[ -gt 0 ] → ❌ 语法错误!# 假设 TRAIN 的值是 "5 "(末尾有空格) [ $TRAIN -gt 0 ] # 替换后:[ 5 -gt 0 ] → ❌ 多了一个参数! -
[ "$VAL" -gt 0 ]: 判断变量VAL的值是否大于0。 -
&&: 逻辑"与",连接多个条件/命令。 -
WITH_DATA=$((WITH_DATA + 1)): 算术运算,将变量WITH_DATA的值加1。
-
【4】脚本中的知识点总结
| 类别 | 语法要素 | 记忆技巧 |
|---|---|---|
| 基础命令 | cd, echo, ls, head, wc, find, cat, tr, cp, mv, grep, od | 记英文原意:cd=换目录 echo=回声 ls=列表 wc=字数统计 cat=串联 tr=转换 cp=复制 mv=移动 grep=全局正则 od=八进制转储 |
| 条件测试 | [ -z "$var" ] [ $a -eq $b ] [ $a -gt $b ] | -z=zero(空吗?) -eq=equal(相等吗?) -gt=greater than(大于吗?) |
| 命令替换 | $(命令) | “先把命令结果算出来,再放这里” 动态获取数据 |
| 算术运算 | $((表达式)) | 数学计算小助手 内部变量不加$ |
| 变量操作 | 变量=值 $变量 | 给数据贴标签,用时喊名字 |
| 错误处理 | 2>/dev/null 命令 && 成功操作 | 错误信息丢黑洞,脚本安静运行 成功才继续的链式操作 |
| 文件安全 | cp 源 目标 mv 源 目标 | 先备份再修改 |
| 字符处理 | tr -d '字符' grep -P '模式' | tr=字符转换器 -d=删除 grep -P=超级搜索模式 |
