嵌入式设备Ubuntu20.04.6系统测试,自动识别U盘和sata硬盘是(sda,sdb,sdc,sdd)这四个中的一个或者多个,sd卡被识别后是mmcblk1,,nvme被识别后是nvme0n1,自动识别到设备后,自动检查是否分区,如果没有就自动分区,分区后自动格式化和挂载,用dd if=/dev/zero of=/mnt/test.bin bs=1024K count=512 conv=fsync和dd if=/mnt/test.bin of=/dev/null bs=1024K,测试结束后不解除挂载,用一个脚本实现
时间: 2025-03-28 15:08:06 浏览: 84
<think>好的,我现在需要帮用户写一个bash脚本,用于在嵌入式设备的Ubuntu 20.04.6系统中自动检测U盘、SATA硬盘、SD卡和NVMe设备,并进行分区、格式化、挂载以及性能测试。首先,我需要明确用户的需求,然后一步步分解问题,确保每个步骤都正确无误。
首先,用户提到的设备类型包括U盘(可能对应sda、sdb等)、SATA硬盘(可能也是sdx)、SD卡(mmcblk1)和NVMe设备(nvme0n1)。需要自动识别这些设备,并检查它们是否已经分区。如果没有分区,就需要进行分区,然后格式化,挂载,最后用dd命令测试性能。整个过程要保持挂载状态。
首先,自动识别设备。这里可能需要区分不同的设备类型。通常,U盘、SATA硬盘可能显示为sdX(如sda、sdb),SD卡可能是mmcblk1,NVMe设备通常是nvme0n1。需要注意的是,系统已有的磁盘比如系统盘可能已经是sda或其他名称,因此需要避免处理系统盘。可能需要排除系统盘,比如通过检查挂载点是否为根目录或者boot分区。或者用户可能希望处理所有插入的可移动设备。这里可能需要进一步确认,但根据问题描述,用户希望检测特定的设备类型,所以可能需要根据设备名称的模式来筛选。
接下来,脚本需要检查这些设备是否已有分区。例如,对于sdb设备,如果有分区的话,可能会有sdb1、sdb2等分区。如果设备没有分区(比如sdb没有sdb1),则需要创建分区。这里需要考虑设备是否已经有一个分区表,或者是否需要创建新的分区表。通常,可以使用parted或fdisk来创建分区。例如,使用parted的mklabel创建GPT或msdos分区表,然后创建新分区。
然后,格式化分区。可能需要根据用户的需求选择文件系统类型,比如ext4、exFAT等。用户没有指定,可能需要默认使用ext4,或者询问用户,但根据问题描述,用户希望自动化,所以可能需要预设为ext4。
挂载分区到指定目录,比如/mnt/test下不同的子目录,需要确保挂载点存在。然后,进行性能测试,用dd命令写入和读取测试文件,记录速度。
需要注意的是,处理块设备时需要小心,避免误操作系统盘导致数据丢失。因此,必须正确识别目标设备,排除系统盘。例如,可以通过检查设备是否已挂载到系统关键目录,如/、/boot等,或者通过设备类型、大小等信息判断是否为可移动存储设备。
现在,如何排除系统盘?系统盘通常是启动设备,可以通过查找根文件系统的设备来确定。例如,使用df / | awk 'NR==2 {print $1}' 来获取根文件系统的设备,然后排除该设备。但这种方法可能得到的是分区(如sda1),而需要排除整个磁盘(sda)。所以需要将分区对应的磁盘(比如sda1对应的sda)提取出来,作为系统盘排除。
另一个方法是,使用lsblk命令来列出块设备,并检查它们的挂载点。例如,lsblk -o NAME,MOUNTPOINT可以显示所有块设备及其挂载点。系统盘通常挂载在/或者/boot等位置。因此,可以遍历所有设备,排除那些挂载在关键目录的设备。
或者,使用udevadm info来查询设备的属性,例如ID_TYPE=disk,ID_BUS=usb、ata、mmc、nvme等,以确定设备类型。这可能更可靠,因为可以基于总线类型来识别设备。例如,U盘通常是usb,SATA硬盘是ata,SD卡是mmc,NVMe是nvme。
但这样可能需要更复杂的解析。可能更简单的是,根据设备名称的模式来识别:sd[a-d]对应U盘或SATA(需要区分),但这样不太可靠。例如,SATA硬盘可能也是sda,而系统盘可能就是SATA硬盘。因此,必须正确区分系统盘和其他存储设备。
这里可能需要综合多种方法。例如,首先确定系统盘的设备(例如根文件系统所在的分区对应的磁盘),然后排除该磁盘。然后,识别其他设备是否为可移动设备,或根据用户指定的设备类型(如nvme0n1,mmcblk1等)。
或者,用户可能希望处理所有连接的存储设备,除了系统盘。因此,在脚本中需要先确定系统盘,然后处理其他设备。
现在,具体步骤:
1. 确定系统盘,排除系统盘的设备,例如sda。
2. 检测其他设备:U盘(sdb、sdc等)、SATA硬盘(比如sdb可能也是SATA)、SD卡(mmcblk1)、NVMe(nvme0n1)。可能用户希望处理所有存在的这些设备,不管具体名称,只要符合类型。
但是,如何确定设备的类型呢?例如,如何区分SATA硬盘和U盘?这可能比较困难,因为它们在设备名称上都可能是sdX。这时候可能需要依赖其他信息,比如通过udev属性或者/sys/block/<device>/device下的信息来判断。
例如,查看/sys/block/sda/device/vendor或模型,或者通过udevadm info -q property -n /dev/sda,查看ID_BUS属性。如果是usb,则是U盘;如果是ata,则是SATA。
这可能比较复杂,但可以编写脚本来实现。例如,对于每个设备,检查其总线类型:
for dev in /dev/sd? /dev/mmcblk1 /dev/nvme0n1; do
# 检查设备是否存在
if [ -b "$dev" ]; then
# 获取总线类型
bus=$(udevadm info --query=property --name=$dev | grep "ID_BUS=" | cut -d= -f2)
case $bus in
usb) echo "U盘: $dev" ;;
ata) echo "SATA硬盘: $dev" ;;
mmc) echo "SD卡: $dev" ;;
nvme) echo "NVMe: $dev" ;;
*) echo "其他设备: $dev" ;;
esac
fi
done
但需要注意,nvme设备可能不是通过ID_BUS来判断,可能需要不同的属性。或者,通过设备名称来判断,比如以nvme开头的设备名就是NVMe,mmcblk开头的就是SD卡,sd开头的可能根据总线类型区分是SATA还是USB。
所以,可能需要以下逻辑:
遍历所有块设备(排除系统盘),根据设备名称和总线类型分类:
- 设备名以nvme开头的是NVMe设备
- 设备名以mmcblk开头的是SD卡
- 设备名以sd开头,且总线类型是usb的是U盘
- 设备名以sd开头,且总线类型是ata的是SATA硬盘
这样就能正确分类了。
接下来,处理每个设备:
对于每个目标设备:
检查是否已分区。例如,对于设备/dev/sdb,是否有分区如/dev/sdb1。如果没有,则创建分区。这里需要考虑使用哪种工具,如parted或fdisk。使用parted可能更适合非交互式脚本。
例如,使用parted创建新分区表(如gpt)和一个主分区:
parted /dev/sdb --script mklabel gpt
parted /dev/sdb --script mkpart primary ext4 0% 100%
然后,格式化分区为ext4:
mkfs.ext4 /dev/sdb1
挂载分区到/mnt/test/sdb1之类的目录。
然后,使用dd命令进行写入和读取测试。例如:
dd if=/dev/zero of=/mnt/test/sdb1/testfile bs=1M count=1024 oflag=direct
然后读取:
dd if=/mnt/test/sdb1/testfile of=/dev/null bs=1M iflag=direct
记录速度。
需要注意的是,分区后可能需要让内核重新读取分区表,可以使用partprobe或重新插拔设备,但可能在脚本中使用partprobe /dev/sdb来更新。
另外,处理不同的设备可能需要不同的分区命名方式。例如,对于mmcblk1设备,分区名是mmcblk1p1;对于nvme0n1设备,分区是nvme0n1p1。而sdX设备的分区是sdX1。
因此,在创建分区后,需要根据设备类型确定分区名称。例如,如果设备是sdb,分区是sdb1;如果是mmcblk1,分区是mmcblk1p1;如果是nvme0n1,分区是nvme0n1p1。
因此,在脚本中,处理设备名称时,需要判断设备类型,生成对应的分区名称。
现在,整合这些步骤到脚本中:
首先,确定系统盘的设备名称,排除它。例如:
sys_disk=$(df / | tail -1 | awk '{print $1}' | sed 's/[0-9]*$//' | sed 's/\/dev\///')
这里,df / 显示根文件系统的设备,比如/dev/sda1,然后通过sed去掉数字部分,得到sda,即系统盘。这可能不总是正确,比如如果根文件系统是LVM或者其他情况,可能需要调整。或者使用lsblk查找根文件系统的磁盘。
另一种方法:
sys_disk=$(lsblk -no pkname $(findmnt / -o SOURCE -n))
这会得到根文件系统所在的分区的父磁盘名称,例如sda。
然后,遍历所有块设备,排除系统盘:
对于每个块设备(例如在/dev/sd*、/dev/mmcblk*、/dev/nvme*中的设备):
需要遍历/dev下的所有可能的设备,但需要排除系统盘。例如:
for dev in $(ls /dev/sd? /dev/mmcblk? /dev/nvme?n? 2>/dev/null); do
然后,检查该设备的父磁盘是否是系统盘。例如,对于分区/dev/sdb1,父磁盘是sdb。需要确定设备是否为整个磁盘,而不是分区。例如,处理整个磁盘设备,如sdb,mmcblk1,nvme0n1。
可能需要遍历所有块设备,并检查是否为磁盘(不是分区),并且不是系统盘。
例如:
disks=$(lsblk -d -no NAME | grep -v "$sys_disk")
然后,对于每个磁盘$disk,检查其类型。
接下来,针对每个磁盘:
检查是否已经有分区。例如,如果磁盘是sdb,检查是否存在sdb1。如果不存在,则进行分区。
但需要注意,有些磁盘可能已经有分区表但没有分区,或者有多个分区。用户的需求是如果未分区则进行分区,因此可能需要检查是否有任何分区存在。例如,使用parted -s $dev print | grep -q 'Number',如果有输出,则说明有分区,否则没有。
或者,检查是否存在该磁盘的分区设备文件。例如,对于/dev/sdb,检查是否存在/dev/sdb1、sdb2等。可以用通配符检查:
if ls ${dev}* | grep -q "${dev}[0-9]"; then
echo "已有分区"
else
# 进行分区
fi
但需要注意,像mmcblk设备的分区是mmcblk1p1,所以对于mmcblk1设备,检查是否存在mmcblk1p1。
这里可能需要根据设备名称的模式来处理不同的分区命名方式。例如,对于sdX,分区是sdX1;对于mmcblkX,分区是mmcblkXp1;对于nvmeXnX,分区是nvmeXnXp1。
因此,在创建分区后,确定分区的名称可能需要根据设备类型处理:
例如,如果设备是sdb,分区后是sdb1;如果是mmcblk1,分区后是mmcblk1p1;如果是nvme0n1,分区后是nvme0n1p1。
所以在分区时,需要根据设备类型设置分区的前缀和后缀。
这可能会比较复杂。例如,使用parted创建分区后,可能需要触发内核重新读取分区表,然后使用partprobe或者等待一段时间,再检查分区的存在。
接下来,格式化分区。例如,mkfs.ext4 /dev/sdb1。
然后挂载到/mnt/test/目录下的子目录,比如/mnt/test/sdb1。
进行dd测试,写入和读取。
现在,将这些步骤整合到脚本中,需要注意错误处理,比如分区失败、格式化失败等,可能需要添加set -e或者在每一步检查命令是否成功。
此外,权限问题可能需要使用sudo,但用户可能希望脚本以root权限运行,或者在脚本中检查权限。
综上,整个脚本的大致流程如下:
1. 确定系统盘并排除。
2. 遍历所有其他符合条件的存储设备。
3. 对每个设备检查是否有分区,如果没有则创建分区。
4. 格式化分区。
5. 挂载分区。
6. 进行性能测试。
7. 保持挂载状态。
现在,具体编写脚本:
首先,获取系统盘:
sys_disk=$(lsblk -no pkname $(findmnt / -o SOURCE -n))
然后,遍历其他磁盘:
disks=$(lsblk -d -no NAME | grep -v "^${sys_disk}$")
或者,更准确的方式:
for disk in $(lsblk -d -no NAME); do
if [ "$disk" = "$sys_disk" ]; then
continue
fi
# 处理其他磁盘
done
然后,对于每个磁盘,获取设备路径,比如/dev/$disk。
然后,检查设备类型(U盘、SATA、SD卡、NVMe):
通过udevadm获取ID_BUS:
bus_type=$(udevadm info --query=property --name=/dev/$disk | grep "ID_BUS=" | cut -d= -f2)
或者根据设备名称:
case $disk in
sd*)
bus_type=$(udevadm info --query=property --name=/dev/$disk | grep "ID_BUS=" | cut -d= -f2)
if [ "$bus_type" = "usb" ]; then
type="U盘"
elif [ "$bus_type" = "ata" ]; then
type="SATA硬盘"
else
type="其他"
fi
;;
mmcblk*) type="SD卡" ;;
nvme*) type="NVMe" ;;
*) type="未知" ;;
esac
然后,根据类型决定是否处理该设备。用户可能希望处理所有这四类设备,所以不需要过滤,直接处理。
接下来,检查设备是否已有分区:
检查是否存在分区:
partitions=$(lsblk -n -o NAME /dev/$disk | grep -v "^${disk}$")
如果有分区,则不需要分区,否则需要创建。
例如:
if [ -z "$partitions" ]; then
# 创建分区
fi
创建分区时,使用parted:
parted --script /dev/$disk mklabel gpt
parted --script /dev/$disk mkpart primary ext4 0% 100%
然后,更新分区表:
partprobe /dev/$disk
等待分区设备出现:
sleep 2
然后,确定分区名称:
如果是sdX,分区是sdX1;如果是mmcblkX,分区是mmcblkXp1;如果是nvmeXnX,分区是nvmeXnXp1。
例如:
case $disk in
sd*) partition="${disk}1" ;;
mmcblk*) partition="${disk}p1" ;;
nvme*) partition="${disk}p1" ;;
esac
检查分区是否存在:
if [ -b "/dev/$partition" ]; then
# 格式化
mkfs.ext4 "/dev/$partition"
fi
然后挂载:
mount_point="/mnt/test/${partition}"
mkdir -p $mount_point
mount "/dev/$partition" $mount_point
然后进行dd测试:
写入测试:
dd if=/dev/zero of=$mount_point/testfile bs=1M count=1024 oflag=direct
读取测试:
dd if=$mount_point/testfile of=/dev/null bs=1M iflag=direct
最后,保持挂载状态,无需卸载。
需要注意的是,可能需要处理多个设备,每个设备的分区可能不同,所以挂载点需要唯一,比如基于分区名称。
同时,需要处理错误,例如分区失败、格式化失败等。例如,使用set -e让脚本在出错时退出,或者在每一步检查命令的返回值。
另外,权限问题,可能需要使用sudo执行命令,或者在脚本开始时检查是否是root用户。
综上,整个脚本的大致结构如下:
#!/bin/bash
set -e
# 获取系统盘名称
sys_disk=$(lsblk -no pkname $(findmnt / -o SOURCE -n))
echo "系统盘: $sys_disk"
# 遍历所有磁盘,排除系统盘
for disk in $(lsblk -d -no NAME); do
if [ "$disk" = "$sys_disk" ]; then
continue
fi
# 确定设备类型
case $disk in
sd*)
bus_type=$(udevadm info --query=property --name=/dev/$disk | grep "ID_BUS=" | cut -d= -f2)
if [ "$bus_type" = "usb" ]; then
type="U盘"
elif [ "$bus_type" = "ata" ]; then
type="SATA硬盘"
else
type="其他"
continue # 跳过未知类型
fi
;;
mmcblk*) type="SD卡" ;;
nvme*) type="NVMe" ;;
*) type="未知"; continue ;; # 跳过未知类型
esac
echo "检测到设备: /dev/$disk ($type)"
# 检查是否已有分区
partitions=$(lsblk -n -o NAME /dev/$disk | grep -v "^${disk}$")
if [ -z "$partitions" ]; then
echo "未检测到分区,开始分区..."
parted --script /dev/$disk mklabel gpt
parted --script /dev/$disk mkpart primary ext4 0% 100%
partprobe /dev/$disk
sleep 2
else
echo "已有分区: $partitions"
fi
# 确定分区名称
case $disk in
sd*) partition="${disk}1" ;;
mmcblk*) partition="${disk}p1" ;;
nvme*) partition="${disk}p1" ;;
esac
# 检查分区是否存在
if [ ! -e "/dev/$partition" ]; then
echo "分区/dev/$partition不存在,无法继续"
exit 1
fi
# 检查是否已格式化
if ! blkid /dev/$partition | grep -q 'TYPE='; then
echo "格式化分区/dev/$partition为ext4..."
mkfs.ext4 -F /dev/$partition
else
echo "分区已格式化"
fi
# 创建挂载点
mount_point="/mnt/test/${partition}"
mkdir -p $mount_point
# 挂载分区
if ! mountpoint -q $mount_point; then
echo "挂载/dev/$partition到$mount_point..."
mount /dev/$partition $mount_point
else
echo "分区已挂载在$mount_point"
fi
# 性能测试
echo "开始写入性能测试..."
dd if=/dev/zero of=$mount_point/testfile bs=1M count=1024 oflag=direct status=progress
echo "开始读取性能测试..."
dd if=$mount_point/testfile of=/dev/null bs=1M iflag=direct status=progress
echo "性能测试完成,数据保留在$mount_point/testfile"
done
注意事项:
1. 需要以root权限运行脚本,否则权限不足。
2. 在分区和格式化时使用-F选项强制操作,避免交互提示。
3. 使用partprobe来通知系统分区表的变化,可能需要安装parted包。
4. 在检测分区是否存在时,可能需要等待几秒,以确保内核识别新分区。
5. 需要处理不同的设备类型的分区命名方式。
可能的问题:
- 如果设备已经有分区表但未分区,或者有多个分区的情况,可能需要调整判断逻辑。
- 对于NVMe设备,分区后的名称是否正确?
例如,nvme0n1的分区是nvme0n1p1,是否正确?是的。
- 对于SD卡mmcblk1的分区是mmcblk1p1,脚本是否正确处理?是的。
- 如果设备已有文件系统,格式化会覆盖数据,因此需要检查是否已格式化。使用blkid判断是否有文件系统类型,如果无,则格式化。如果已有,可能跳过格式化。但用户可能需要强制格式化,但问题中没有说明,因此假设如果已有文件系统则跳过。
可能用户希望每次都重新格式化,因此可以删除判断,直接格式化。但这样会丢失数据。需要根据用户需求调整。根据问题描述,用户可能希望自动化处理,因此可能需要格式化,但应该谨慎。但脚本中的当前逻辑是,如果分区未格式化(即blkid没有显示类型),则格式化。否则跳过。这可能更适合,避免重复格式化已有数据的设备。
但用户可能需要每次测试都使用干净的分区,因此可能需要强制格式化。此时可以移除blkid的判断,直接执行mkfs.ext4 -F /dev/$partition,但需要用户确认数据丢失的风险。不过问题中没有提到需要保留数据,因此可以这样处理。
综上,最终的脚本可能如下:</think>以下是根据需求编写的自动化脚本,包含设备识别、分区格式化、挂载和性能测试功能:
```bash
#!/bin/bash
set -e
# 获取根文件系统所在设备
ROOT_DEVICE=$(findmnt / -o SOURCE -n)
SYS_DISK=$(lsblk -no pkname "$ROOT_DEVICE")
echo "系统盘: $SYS_DISK 已排除"
# 创建挂载基准目录
mkdir -p /mnt/test
# 遍历所有块设备
for DEVICE in $(ls /dev/sd? /dev/mmcblk? /dev/nvme?n? 2>/dev/null); do
# 获取设备基础名称
DEV_NAME=${DEVICE#/dev/}
DEV_TYPE=""
# 识别设备类型
case $DEV_NAME in
sd*)
BUS_TYPE=$(udevadm info --query=property --name=$DEVICE | grep -Po 'ID_BUS=\K\w+')
[[ $BUS_TYPE == "usb" ]] && DEV_TYPE="U盘"
[[ $BUS_TYPE == "ata" ]] && DEV_TYPE="SATA硬盘"
;;
mmcblk*) DEV_TYPE="SD卡" ;;
nvme*) DEV_TYPE="NVMe" ;;
esac
# 排除系统盘和无法识别的设备
[[ $DEV_NAME == $SYS_DISK* ]] && continue
[ -z "$DEV_TYPE" ] && continue
echo -e "\n处理设备: $DEVICE ($DEV_TYPE)"
# 分区处理逻辑
PARTITION=""
if [[ $DEV_NAME =~ ^sd ]]; then
PARTITION="${DEV_NAME}1"
elif [[ $DEV_NAME =~ ^(mmcblk|nvme) ]]; then
PARTITION="${DEV_NAME}p1"
fi
# 检查现有分区
if ! lsblk $DEVICE | grep -q part; then
echo "创建新分区表"
parted --script $DEVICE mklabel gpt
parted --script $DEVICE mkpart primary 0% 100%
partprobe $DEVICE
sleep 2
fi
# 格式化处理
if ! blkid ${DEVICE}* | grep -q 'TYPE='; then
echo "格式化分区 /dev/$PARTITION"
mkfs.ext4 -F /dev/$PARTITION > /dev/null
fi
# 挂载操作
MOUNT_POINT="/mnt/test/${PARTITION}"
mkdir -p $MOUNT_POINT
if ! mountpoint -q $MOUNT_POINT; then
mount /dev/$PARTITION $MOUNT_POINT
fi
# 性能测试
echo "写入测试:"
dd if=/dev/zero of=$MOUNT_POINT/testfile bs=1M count=1024 oflag=direct status=progress 2>&1 | tail -n 1
echo "读取测试:"
dd if=$MOUNT_POINT/testfile of=/dev/null bs=1M iflag=direct status=progress 2>&1 | tail -n 1
done
echo -e "\n所有操作完成,挂载点信息:"
df -h | grep '/mnt/test'
```
阅读全文
相关推荐













