答案1
Debian 派生的标准工具是Disk Usage Analyser
( baobab
),但是它不会显示碎片。
我知道显示碎片的唯一方法是逐个文件地使用命令行进程,使用中描述的工具之一这个答案。
为此,我编写了一个脚本hdparm --fibmap
:-
#!/bin/bash
#
# frags MountPoint SkipFrags Reports all files under MountPoint with more than SkipFrags fragments
# MountPoint defaults to / (root file system)
# SkipFrags defaults to 1 (report all files with more than one fragment)
# (setting to 0 will report all files, including unfragmanted)
#
# The report is sorted in fragment count order and must be run as root
#
[ "${1:0:1}" == "-" ] && \
echo 'frags MountPoint SkipFrags Reports all files under MountPoint with more than SkipFrags fragments' && \
echo ' MountPoint defaults to / (root file system)' && \
echo ' SkipFrags defaults to 1 (report all files with more than one fragment)' && \
echo ' (setting to 0 will report all files, including unfragmanted)' && \
echo 'The report is sorted in fragment count order and must be run as root' && \
{ return 1 2>/dev/null; exit 1; }
[ "$(whoami)" != "root" ] && { echo 'Must be run from "root"' >&2; return 13 2>/dev/null; exit 13; }
eof='***EOF***' ; # End-of-file marker
{ find "$1"/ -mount -type f; echo "$eof"; } | \
while read -r f; \
do [ "$f" == "$eof" ] && { echo -ne "\e[K" >&2; continue; }; \
r=$(hdparm --fibmap "$f" | grep -E "^[ 0-9]+$" | wc -l); \
[ $r -gt "${2:-1}" ] && { echo -ne "\e[K--- $r: ${f::$(($COLUMNS-${#r}-6))}\r" >&2; echo "$r: $f"; } \
done | \
sort -nt :
请注意,虽然我已将脚本记录为从其安装点扫描整个磁盘,但您可以获得特定目录的排序结果,并且我建议首先在小目录上执行此操作,以确保它给出合理的结果。
这个过程相当慢,而且排序意味着在扫描完每个文件之前你不会得到任何结果,所以我在stderr
找到每个要报告的文件时添加了一个诊断。转义序列是 forxterm
并且依赖于由in (或另一个初始化文件)$COLUMNS
设置。避免换行可以防止脚本因滚动而变慢。shopt -s checkwinsize; export COLUMNS
~/.bashrc
我使用了[ ... ] &&
而不是if [ ... ]; then
来减少嵌套并缩短脚本。该return 1 2>/dev/null; exit 1;
序列是我学到的一个技巧,无论脚本是正常调用还是通过.
/source
命令调用,它都会返回错误代码。
答案2
我通常用 Python 编写脚本,所以如果有人对 AFH 答案的 Python 版本感兴趣。我的方法取决于命令filefrag
。这将从 cli 参数开始遍历目录树root
,并收集文件名、文件大小和块范围。如果需要,添加参数来指定块大小等可能会很方便。
import subprocess
import os
import re
import argparse
size = re.compile("(\d+) \(\d+ block")
def get_extents(root):
for root, dirs, files in os.walk(root):
for f in files:
fullpath = os.path.abspath(os.path.join(root, f))
output = subprocess.check_output(["filefrag", "-e", "-v", fullpath]).decode("utf-8")
lines = output.splitlines()
sizeline = lines[1]
extentlines = lines[3:-1]
print(fullpath)
print("size in bytes:", size.search(sizeline).group(1))
extents = []
for extentline in extentlines:
stuff = extentline.split()
lower = stuff[3].strip(".")
upper = stuff[4].strip(":")
extents.append((lower, upper))
print("extents:", extents)
if __name__ == "__main__":
parser = argparse.ArgumentParser("get the extents of all the files")
parser.add_argument("root", type=os.path.expanduser, help="the root folder to examine recursively on")
args = parser.parse_args()
get_extents(args.root)