如何在linux上获取磁盘使用情况?


0

我想在硬盘上创建一个写入文件的地图。它应该输出类似于目录树的内容,并为每个文件输出组成整个文件的扇区列表([10-20],[80-90],[60-70])。例如,创建像旧学校Windows磁盘碎片整理程序的可视化:

windows碎片整理

有没有可用的工具?


1
Debian派生的标准工具是Disk Usage Analyserbaobab)。这不会显示碎片,但对于单个文件(而不是目录),您可以使用本答案中描述的工具之一显示它占用的扇区。我不知道GUI工具。
AFH

“显示它所占用的扇区”......为了创建我的可视化,假设我有一个占用扇区但不占用整个扇区的文件(例如,跨两个4096字节扇区写入的8114字节文件); 假设第一个扇区被完全占用,第二个扇区占用了第一个扇区(8114 - 4096),第二个扇区的剩余(4096 * 2-8114)个字节是“垃圾”,而不是其他一些档案?
斯科特

1
是的,我认为情况就是这样。我在80年代学习了归档系统,当时操作系统是Unix,而不是Linux。我记得,为了节省浪费的空间,小文件的一个选项是将几个打包到一个物理扇区,虽然我现在不记得是什么构成一个小文件(可能高达128或256字节),但他们没有' t将多扇区文件的可用空间用于小文件。我不知道现代的extN文件系统是否使用了类似的结构,但如果它们在Linux中丢失了,那将是一个遗憾。大文件再次具有不同的结构,即使在80年代也允许TB文件!
AFH

您在评论中使用的语言帮助我搜索!以下是我的评论问题的答案: stackoverflow.com/questions/30133149 / ...
Scott

1
有趣的链接。回到你原来的问题,我读过的大部分内容都表明碎片在extN文件系统上通常不是问题,尽管我的测试发现奇数文件包含数千个碎片。我所做的是编写一个脚本,找到每个文件,计算每个文件中的片段数,然后按片段计数顺序排序到报告中:使其大小可管理,省略所有未分段的文件。如果这看起来像是一个解决方案,我会在答案中提交脚本,因为它对于评论来说太复杂了。
AFH

Answers:


2

Debian派生的标准工具是Disk Usage Analyserbaobab),但这不会显示碎片。

我知道显示碎片的唯一方法是使用本答案中描述的工具之一逐个文件地使用命令行过程。

为此,我写了一个脚本使用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每个要报告的文件都添加了诊断。换码序列为xterm与依赖于$COLUMNS由被设置shopt -s checkwinsize; export COLUMNS~/.bashrc(或其他初始化文件)。避免换行会阻止脚本通过滚动来减慢速度。

我使用[ ... ] &&而不是if [ ... ]; then为了减少嵌套和缩写脚本。该return 1 2>/dev/null; exit 1;序列是一招我拿起返回错误代码脚本是否正常或者从一个名为./ source命令。


我刚刚在find命令中添加了一个重要参数:-mount停止遍历其他已安装的文件系统,这会产生错误,尤其是在已挂载的网络共享上。
AFH

进一步的测试表明,该脚本可以/在其他物理挂载上运行,例如/boot/efi,但不能在网络共享上,也不能在某些文件系统类型上运行,例如tmpfs。我不知道如何处理它们,或者至少给出一个不支持文件系统的错误。
AFH

0

我通常在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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.