根据 Open Group 规范,POSIXdu
没有-b
以字节为单位显示大小的选项。那么,获取文件或文件夹大小(以字节为单位)的 POSIX 兼容方法是什么?
答案1
du
作为 GNU对 所做的近似操作-sb
,您可以这样做:
cumulative_size() (
export LC_ALL=C
ret=0
[ "$#" -gt 0 ] || set .
for file do
case $file in
(/*) sanitized=$file;;
(*) sanitized=./$file;;
esac
size=$(
find "$sanitized" ! -type b ! -type c -exec ls -niqd {} + |
awk '! seen[$1]++ {sum += $6}
END {print sum}'
)
if [ -n "$size" ]; then
printf '%s\t%s\n' "$size" "$file"
else
ret=1
fi
done
exit "$ret"
)
像 GNU 一样,du
我们尝试通过查看文件的 inode 编号(如 的第一个字段中报告的那样ls -ni
)仅对文件进行一次计数,但由于我们没有设备编号无法ls
报告,假设目录层次结构不跨越多个文件系统。
与此相反,du
我们也只在每个文件参数中进行重复数据删除。
例如在:
cumulative_size dir dir
及其内容的累积磁盘使用情况dir
会报告两次,每个文件中的文件仅计数一次,而 GNUdu -bs
只会报告dir
一次磁盘使用情况。
我们排除设备文件,因为ls -n
不报告其大小。至少在 Linux 上,这不会产生任何影响,因为它们的大小总是报告为0
.
find
不能给出以以下开头-
或名称与其谓词匹配的文件路径(也包括!
, ...)。(
在这里,我们通过在文件路径./
不以/
、so find !
、find -print
becomesfind ./!
或开头的情况下添加前缀来解决这个问题find ./-print
。假设没有find
实现具有以 开头的谓词/
。这意味着我们也不需要传递 to--
来ls
标记其选项的结束。
我们使用ls -n
not 来ls -l
避免将 uids/gids 解码为用户名或组名(这会很昂贵,并且还会导致带有空格的名称出现问题)。 POSIX 指定-o
/-g
选项来完全删除这些字段,但它们是可选的。
的输出ls -n
仅在 C/POSIX 语言环境中指定。此外,文件路径是非空字节的任意序列,您只能将它们作为 C 语言环境中的文本进行处理,因此LC_ALL=C
.
我们还使用它-q
来确保文件名或符号链接目标中的换行符不会造成影响。
另请注意,由于完整路径传递给ls
,我们无法处理任意深度的目录结构,因为一旦路径长度超过 PATH_MAX ,它将停止工作。
错误报告相当粗糙。如果任何计算的大小返回空,我们仅报告非零退出状态。因此,零退出状态并不能保证所有文件大小都已计算在内。
答案2
不幸的是, 的输出格式ls
显然没有标准化。因此解析其输出可能不是最好的主意。
另一种符合 POSIX 标准的查找单个文件大小(以字节为单位)的方法是使用wc -c
:
-c
将每个输入文件中的字节数写入标准输出。
来源:https://pubs.opengroup.org/onlinepubs/9699919799/utilities/wc.html
$ printf %s 0123456789ABCDEF >sixteenbytestestfile # example file of 16 bytes length
$ wc -c sixteenbytestestfile
16 sixteenbytestestfile
如果我们不将文件作为参数传递,而是通过标准输入传递,则输出中将省略文件名:
$ wc -c <sixteenbytestestfile
16
显然,某些系统在数字输出周围添加了一些空格。我们可以使用以下方法删除它算术扩展没有任何算术运算:
$ filesize=" 123 " # possible wc -c output
$ printf %s\\n "-$filesize-"
- 123 -
$ printf %s\\n "-$((filesize))-"
-123-
总之,这里是一个获取文件大小的简单函数的定义:
$ filesize() { printf %s\\n "$(($(wc -c <"$1")))"; }
$ filesize sixteenbytestestfile
16