我有一个Python脚本,可以读取标记不可读扇区的文件(通常来自光学介质),以允许重新尝试在不同的光学读取器上读取所述不可读扇区。
我发现我的脚本无法使用块设备(例如 /dev/sr0)来创建包含的 ISO9660/UDF 文件系统的副本,因为
os.stat().st_size
为零。该算法目前需要提前知道文件大小;我可以更改它,但是问题(了解块设备大小)仍然存在,并且这里没有得到解答,所以我打开这个问题。
我知道以下两个相关的问题:
因此,我想问:在Python中,如何获取块设备文件的文件大小?
我所达到的“最干净”(即不依赖于外部卷且最可重用)的Python解决方案是打开设备文件并在末尾查找,返回文件偏移量:
def get_file_size(filename):
"Get the file size by seeking at end"
fd= os.open(filename, os.O_RDONLY)
try:
return os.lseek(fd, 0, os.SEEK_END)
finally:
os.close(fd)
Linux 特定的基于 ioctl 的解决方案:
import fcntl
import struct
device_path = '/dev/sr0'
req = 0x80081272 # BLKGETSIZE64, result is bytes as unsigned 64-bit integer (uint64)
buf = b' ' * 8
fmt = 'L'
with open(device_path) as dev:
buf = fcntl.ioctl(dev.fileno(), req, buf)
bytes = struct.unpack('L', buf)[0]
print device_path, 'is about', bytes / (1024 ** 2), 'megabytes'
当然,其他 Unix 系统的 req、buf、fmt 会有不同的值。
另一种可能的解决方案是
def blockdev_size(path):
"""Return device size in bytes.
"""
with open(path, 'rb') as f:
return f.seek(0, 2) or f.tell()
or f.tell()
部分是为了 Python 2 可移植性而存在的 — file.seek() 在 Python 2 中返回 None。
魔法常数
2
可以替换为 io.SEEK_END
。
在Linux中,有
/sys/block/${dev}/size
即使没有sudo也可以读取。要获取 /dev/sdb
的大小,只需执行以下操作:
print( 512 * int(open('/sys/block/sdb/size','r').read()) )
此解决方案使用lsblk(由util-linux提供)并且不需要sudo:
import json
import subprocess
def get_device_info(device: str):
output = subprocess.run(
["lsblk", device, "--json", "--bytes"], capture_output=True, check=True
)
dev_info = json.loads(output.stdout)
size_bytes = dev_info["blockdevices"][0]["size"]
return size_bytes
尝试适应其他答案:
import fcntl
c = 0x00001260 ## check man ioctl_list, BLKGETSIZE
f = open('/dev/sr0', 'ro')
s = fcntl.ioctl(f, c)
print s
我手头没有合适的计算机来测试这一点。我很想知道它是否有效:)