我有以下代码作为 shell 脚本的一部分:
while [ $(ps -ef | awk '{print $2}' | grep -F "$CPPID") ]; do
sleep 10
awk -v "usbsize=$(/bin/df | grep -F $DEVICEMOUNTPOINTQ | awk '{print $3}')" -v "isosize=$(/bin/df | grep -F $ISOMOUNTPOINTQ | awk '{print $3}')" 'BEGIN { printf "%.1f", 100 * usbsize / isosize }' && echo "% copied..."
done
这是监控cp
正在执行以下操作:
cp -a "$ISOMOUNTPOINT"/* "$DEVICEMOUNTPOINT"
在大多数情况下,这种方法都行得通,直到
90.5% copied...
94.2% copied...
97.8% copied...
101.6% copied...
102.7% copied...
为什么这会超过源大小的 100%?复制是从循环挂载的 ISO 到 USB 闪存驱动器上的 NTFS 格式分区。我猜这可能是文件系统的问题?
我的示例缺少什么来使尺寸匹配,以便cp
完成时复制的是 100%,而不是 103%?
谢谢。
回复:赏金
我将向第一个提出类似于上述代码并满足以下标准的解决方案的人颁发赏金:
- 脚本必须能够检测 1:1 比例的复制
- 脚本不得显示超过 100% 复制的值,然而...
- 当超出复制量时,脚本不能简单地将显示限制在 100%。
如果数据大小做由于某种原因,源与目标确实有所不同,那么我想要一个能够注意到这一点并仍然显示实际复制比例的脚本。
答案1
以下是简化版且更易读的代码:
while ps -p $CPPID > /dev/null
do
sleep 10
usbsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk 'NR == 2 {print $3}')
isosize=$(/bin/df $ISOMOUNTPOINTQ | awk 'NR == 2 {print $3}')
awk -v "usbsize=$usbsize" -v "isosize=$isosize" 'BEGIN { printf "%.1f%% copied...\n", 100 * usbsize / isosize }'
done
最后awk
一行可以用这两行替换:
percent=$(echo "$usbsize / $isosize * 100" | bc -l)
printf "%.1f%% copied...\n" $percent
printf
然后你可以在该语句之前执行以下操作:
if (( $(echo "$percent > 100" | bc) == 1 ))
then
break
fi
wait $CPPID
并在循环结束后添加while
。一旦达到 100%,打印进度就会停止。
看流程管理关于 PID 的可靠性(它们会被回收利用)。
您看到的问题可能是由于使用目标文件系统的“已使用”值,而不是不同之处从起始值开始计算当前值。
尝试在循环之前添加如下一行while
:
startsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk 'NR == 2 {print $3}')
并将循环内的行更改为:
usbsize=$(/bin/df $DEVICEMOUNTPOINTQ | awk -v "start=$startsize" 'NR == 2 {print $3 - start}')
rsync --progress
当然,如果您使用而不是 ,这一切也许都可以避免cp
。
编辑:
另外,请尝试在while
循环中执行上述操作,以查看计算中使用的数字。这可能会提供一些线索,说明发生了什么:
awk -v "usbsize=$usbsize" -v "isosize=$isosize" 'BEGIN { printf "%d of %d, %.1f%% copied...\n", usbsize, isosize, 100 * usbsize / isosize }'
答案2
我的第一印象是,这在很大程度上取决于源目录中的文件类型。我认为可能的罪魁祸首是稀疏文件。稀疏文件是 stat.st_size != (stat.st_blksize * stat.st_blocks) 的文件;也就是说,文件的整体大小大于与文件的 inode 关联的数据块数。系统调用会将任何未分配的块读为零块。因此,当您在稀疏文件上使用 cp(1) 时,目标文件将包含比源文件更多的块(仅包含零)。du(1) 和 df(1) 命令查看块的数量,而不是文件的大小。核心文件通常创建为稀疏文件,因为它们可能需要映射内存。这种类型的文件对于创建磁盘映像很有用,例如创建大小为 15GB 的虚拟主机驱动器。在创建时分配所有块会非常浪费;大小(st_size)可以是15GB,但实际的块数可以从0开始。
这只是复制时可能爆炸的文件类型之一。如果不知道文件系统中有什么文件,就很难说还有什么文件可能导致这种现象。
答案3
您可以在本地模式下使用 rsync,其中源和目标的名称中都没有“:”,因此它的行为就像一个改进的复制命令。使用进度参数,它会显示类似于以下内容的内容(来源):
$ rsync -r -v --progress -e ssh root@remote-server:~/pictures /home/user/
receiving file list ...
366 files to consider
pictures/IMG_1142.jpg
4400662 100% 32.21kB/s 0:02:13 (xfer#31, to-check=334/366)
pictures/IMG_1172.jpg
2457600 71% 32.49kB/s 0:00:29
由于这没有给出总百分比,另一个解决方案可能是使用此脚本(来源):
#!/bin/sh
cp_p()
{
strace -q -ewrite cp -- "${1}" "${2}" 2>&1 \
| awk '{
count += $NF
if (count % 10 == 0) {
percent = count / total_size * 100
printf "%3d%% [", percent
for (i=0;i<=percent;i++)
printf "="
printf ">"
for (i=percent;i<100;i++)
printf " "
printf "]\r"
}
}
END { print "" }' total_size=$(stat -c '%s' "${1}") count=0
}
实际操作:
% cp_p /mnt/raid/pub/iso/debian/debian-2.2r4potato-i386-netinst.iso /dev/null
76% [===========================================> ]
您还可以查看带进度条移动文件详细说明如何在 cp 和 mv 中添加 -g 开关来显示进度。