kopia lustrzana https://github.com/dhylands/rshell
Implement df command using os.statvfs
rodzic
c8a2cd7d20
commit
1a7b59222e
19
README.rst
19
README.rst
|
@ -378,6 +378,25 @@ and destination, it will only be copied if the source is newer than the
|
|||
destination.
|
||||
|
||||
|
||||
df
|
||||
--
|
||||
|
||||
::
|
||||
|
||||
usage: df [-b|-h|-H]
|
||||
|
||||
Report file system space usage
|
||||
|
||||
optional arguments:
|
||||
--help show this help message and exit
|
||||
-b, --bytes Prints sizes in bytes
|
||||
-h, --human-readable Prints sizes in a human-readable format using power of 1024
|
||||
-H, --si Prints sizes in a human-readable format using power of 1000
|
||||
|
||||
Gets filesystem available space based on statvfs. Granularity is limited
|
||||
to filesystem block size.
|
||||
|
||||
|
||||
echo
|
||||
----
|
||||
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""Implements tooling for formatting df command columns"""
|
||||
|
||||
from enum import Enum
|
||||
|
||||
|
||||
def convert_bytes(size, unit = '', always_append_unit = False):
|
||||
"""Converts size in bytes to closest power of 1024: Ki, Mi, Gi, etc.
|
||||
"""
|
||||
single_unit = '' if always_append_unit else unit
|
||||
appendix = unit if always_append_unit else ''
|
||||
for x in [single_unit, 'Ki', 'Mi', 'Gi', 'Ti']:
|
||||
if size < 1024.0:
|
||||
return "%3.1f%s%s" % (size, x, appendix)
|
||||
size /= 1024.0
|
||||
|
||||
return size
|
||||
|
||||
def convert_bytes_si(size, unit = '', always_append_unit = False):
|
||||
"""Converts size in bytes to closest power of 1000: K, M, G, etc.
|
||||
"""
|
||||
single_unit = '' if always_append_unit else unit
|
||||
appendix = unit if always_append_unit else ''
|
||||
for x in [single_unit, 'K', 'M', 'G', 'T']:
|
||||
if size < 1000.0:
|
||||
return "%3.1f%s%s" % (size, x, appendix)
|
||||
size /= 1000.0
|
||||
|
||||
return size
|
||||
|
||||
|
||||
class DByteFormat(Enum):
|
||||
"""Enum for selecting the formatting for size in bytes
|
||||
"""
|
||||
|
||||
BYTES = 1
|
||||
"""Output in bytes
|
||||
"""
|
||||
|
||||
HUMAN = 2
|
||||
"""Output in human readable format: powers of 1024
|
||||
"""
|
||||
|
||||
HUMAN_SI = 3
|
||||
"""Output in human readable format: powers of 1000
|
||||
"""
|
||||
|
||||
|
||||
class DfColumn:
|
||||
|
||||
def title(self):
|
||||
pass
|
||||
|
||||
def formatted(statvfs, dev_name, dir):
|
||||
pass
|
||||
|
||||
|
||||
class DfNumColumn(DfColumn):
|
||||
|
||||
def __init__(self, num_format = '{:d}'):
|
||||
self.num_format = num_format
|
||||
|
||||
def formatted(self, statvfs, dev_name, dir):
|
||||
value = self.get_num_value(statvfs)
|
||||
return self.num_format.format(value)
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
pass
|
||||
|
||||
|
||||
class DfByteColumn(DfNumColumn):
|
||||
|
||||
def __init__(self, byte_format):
|
||||
self.byte_format = byte_format
|
||||
super().__init__('{:d}B')
|
||||
|
||||
def formatted(self, statvfs, dev_name, dir):
|
||||
value = self.get_num_value(statvfs)
|
||||
if self.byte_format == DByteFormat.HUMAN:
|
||||
return convert_bytes(value, 'B')
|
||||
elif self.byte_format == DByteFormat.HUMAN_SI:
|
||||
return convert_bytes_si(value, 'B')
|
||||
else: # fallback to bytes as default
|
||||
return super().formatted(statvfs, dev_name, dir)
|
||||
|
||||
|
||||
class DfFilesystem(DfColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Filesystem'
|
||||
|
||||
def formatted(self, statvfs, dev_name, dir):
|
||||
return '{:s}@{:s}'.format(dir[:-1], dev_name)
|
||||
# format: /${dir_name}/@${device_name}
|
||||
# e.g. /flash@pyboard
|
||||
|
||||
|
||||
class DfMountedOn(DfColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Mounted on'
|
||||
|
||||
def formatted(self, statvfs, dev_name, dir):
|
||||
return '/{}{}'.format(dev_name, dir)[:-1]
|
||||
# format: /${device_name}/${dir_name}
|
||||
# e.g. /pyboard/flash
|
||||
|
||||
|
||||
class DfNumBlocks(DfNumColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Blocks'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[2]
|
||||
# f_blocks
|
||||
|
||||
|
||||
class DfBlockSize(DfNumColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Block size'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[1]
|
||||
# f_frsize
|
||||
|
||||
|
||||
class DfUsedBlocks(DfNumColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Used'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[2] - statvfs[3]
|
||||
# f_blocks - f_used
|
||||
|
||||
|
||||
class DfAvailBlocks(DfNumColumn):
|
||||
|
||||
def title(self):
|
||||
return 'Available'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[4]
|
||||
# f_bavail
|
||||
|
||||
|
||||
class DfCapacityBlocks(DfNumColumn):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('{:.0f}%')
|
||||
|
||||
def title(self):
|
||||
return 'Capacity'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return 100 * (statvfs[2] - statvfs[3]) / statvfs[2] if statvfs[2] > 0 else 0
|
||||
# 100 * (f_blocks - f_used) / f_blocks
|
||||
# or 0 if 0 blocks
|
||||
|
||||
|
||||
class DfSizeBytes(DfByteColumn):
|
||||
|
||||
def __init__(self, byte_format):
|
||||
super().__init__(byte_format)
|
||||
|
||||
def title(self):
|
||||
return 'Size'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[1] * statvfs[2]
|
||||
# f_frsize * f_blocks
|
||||
|
||||
|
||||
class DfUsedBytes(DfByteColumn):
|
||||
|
||||
def __init__(self, byte_format):
|
||||
super().__init__(byte_format)
|
||||
|
||||
def title(self):
|
||||
return 'Used'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[1] * (statvfs[2] - statvfs[3])
|
||||
# f_frsize * (f_blocks - f_used)
|
||||
|
||||
|
||||
class DfAvailBytes(DfByteColumn):
|
||||
|
||||
def __init__(self, byte_format):
|
||||
super().__init__(byte_format)
|
||||
|
||||
def title(self):
|
||||
return 'Available'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return statvfs[1] * statvfs[4]
|
||||
# f_frsize * f_bavail
|
||||
|
||||
class DfCapacityBytes(DfNumColumn):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('{:.0f}%')
|
||||
|
||||
def title(self):
|
||||
return 'Capacity'
|
||||
|
||||
def get_num_value(self, statvfs):
|
||||
return 100 * (statvfs[2] - statvfs[3]) / statvfs[2] if statvfs[2] > 0 else 0
|
||||
# 100 * (f_blocks - f_used) / f_blocks
|
||||
# or 0 if 0 blocks
|
||||
|
||||
|
||||
def create_byte_sizes_columns(byte_format):
|
||||
"""Returns standard set of columns for df command output
|
||||
in bytes in different formats
|
||||
"""
|
||||
return [
|
||||
DfFilesystem(),
|
||||
DfSizeBytes(byte_format),
|
||||
DfUsedBytes(byte_format),
|
||||
DfAvailBytes(byte_format),
|
||||
DfCapacityBytes(),
|
||||
DfMountedOn(),
|
||||
]
|
||||
|
||||
|
||||
def create_block_sizes_columns():
|
||||
"""Returns standard set of columns for df command output
|
||||
in blocks
|
||||
"""
|
||||
return [
|
||||
DfFilesystem(),
|
||||
DfBlockSize(),
|
||||
DfNumBlocks(),
|
||||
DfUsedBlocks(),
|
||||
DfAvailBlocks(),
|
||||
DfCapacityBlocks(),
|
||||
DfMountedOn(),
|
||||
]
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
import sys
|
||||
try:
|
||||
import rshell.dfutils as dfutils
|
||||
from rshell.getch import getch
|
||||
from rshell.pyboard import Pyboard, PyboardError
|
||||
from rshell.version import __version__
|
||||
|
@ -713,6 +714,15 @@ def eval_str(string):
|
|||
return output
|
||||
|
||||
|
||||
def get_vfs_stats(filename):
|
||||
"""Returns filesystem statistics."""
|
||||
import os
|
||||
try:
|
||||
return os.statvfs(filename)
|
||||
except OSError:
|
||||
return -1
|
||||
|
||||
|
||||
def get_filesize(filename):
|
||||
"""Returns the size of a file, in bytes."""
|
||||
import os
|
||||
|
@ -2006,7 +2016,8 @@ class Shell(cmd.Cmd):
|
|||
parser = argparse.ArgumentParser(
|
||||
prog=command,
|
||||
usage='\n'.join(usage),
|
||||
description='\n'.join(description)
|
||||
description='\n'.join(description),
|
||||
conflict_handler='resolve'
|
||||
)
|
||||
for args, kwargs in argparse_args:
|
||||
parser.add_argument(*args, **kwargs)
|
||||
|
@ -2581,6 +2592,70 @@ class Shell(cmd.Cmd):
|
|||
if len(files) > 0:
|
||||
print_cols(sorted(files), self.print, self.columns)
|
||||
|
||||
argparse_df = (
|
||||
add_arg(
|
||||
'-b', '--bytes',
|
||||
dest='byte_sizes',
|
||||
action='store_true',
|
||||
help='Prints sizes in bytes',
|
||||
default=False
|
||||
),
|
||||
add_arg(
|
||||
'-h', '--human-readable',
|
||||
dest='human_readable',
|
||||
action='store_true',
|
||||
help='Prints sizes in a human-readable format using power of 1024',
|
||||
default=False
|
||||
),
|
||||
add_arg(
|
||||
'-H', '--si',
|
||||
dest='human_readable_si',
|
||||
action='store_true',
|
||||
help='Prints sizes in a human-readable format using power of 1000',
|
||||
default=False
|
||||
),
|
||||
)
|
||||
|
||||
def complete_df(self, text, line, begidx, endidx):
|
||||
return self.filename_complete(text, line, begidx, endidx)
|
||||
|
||||
|
||||
def do_df(self, line):
|
||||
"""df [-b|-h|-H]
|
||||
|
||||
Report file system space usage
|
||||
"""
|
||||
args = self.line_to_args(line)
|
||||
|
||||
if args.byte_sizes:
|
||||
columns = dfutils.create_byte_sizes_columns(dfutils.DByteFormat.BYTES)
|
||||
elif args.human_readable:
|
||||
columns = dfutils.create_byte_sizes_columns(dfutils.DByteFormat.HUMAN)
|
||||
elif args.human_readable_si:
|
||||
columns = dfutils.create_byte_sizes_columns(dfutils.DByteFormat.HUMAN_SI)
|
||||
else:
|
||||
columns = dfutils.create_block_sizes_columns()
|
||||
|
||||
table = []
|
||||
widths = [len(col.title()) for col in columns]
|
||||
with DEV_LOCK:
|
||||
for dev in DEVS:
|
||||
for dir in dev.root_dirs:
|
||||
stats = dev.remote_eval(get_vfs_stats, dir)
|
||||
row = [col.formatted(stats, dev.name, dir) for col in columns]
|
||||
table.append(row)
|
||||
widths = [max(len(val), widths[i]) for i, val in enumerate(row)]
|
||||
col_formatters = []
|
||||
for i, width in enumerate(widths):
|
||||
# first and last row should be aligned to the left, others to the right
|
||||
alignment = '<' if i == 0 or i == len(widths) - 1 else '>'
|
||||
col_formatters.append('{:' + alignment + str(width) + 's}')
|
||||
row_f = " ".join(col_formatters)
|
||||
print(row_f.format(*[col.title() for col in columns]))
|
||||
for row in table:
|
||||
print(row_f.format(*row))
|
||||
|
||||
|
||||
def complete_mkdir(self, text, line, begidx, endidx):
|
||||
return self.filename_complete(text, line, begidx, endidx)
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue