人类可读的文件大小和行数
Human-readable filesize and line count
我想要一个 bash 命令,它将 return 一个 table,其中每一行都是人类可读的文件大小、行数和文件名。 table 应按文件大小排序。
我一直在尝试结合使用 du -hs
、wc -l
、sort -h
和 find
。
这是我所在的位置:
find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h
问题是您的 shell 解释了 $(...)
,所以 find
没有理解它们。转义它们也无济于事 ($\(du -h {}\)
),因为它们成为命令的正常参数,而不是命令替换。
为了将它们解释为命令替换是调用一个新的shell,或者直接
find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h
或通过创建脚本并从 find
调用它。
好的,我也用 find/-exec 试过了,但是转义很麻烦。使用 shell 函数,它的工作非常简单:
#!/bin/bash
function dir
{
du=$(du -sh "" | awk '{print }')
wc=$(wc -l < "")
printf "%10s %10s %s\n" $du $wc "${1#./}"
}
printf "%10s %10s %s\n" "size" "lines" "name"
OIFS=$IFS; IFS=""
find . -type f -print0 | while read -r -d $'[=10=]' f; do dir "$f"; done
IFS=$OIFS
使用 basishm read 通过使用 nul 终止符甚至是安全的。需要 IFS 来避免读取以截断文件名中的尾随空白。
顺便说一句:$'[=12=]'
并没有真正起作用(与 ''
相同)- 但它使意图明确。
示例输出:
size lines name
156K 708 sash
16K 64 hostname
120K 460 netstat
40K 110 fuser
644K 1555 dir/bash
28K 82 keyctl
2.3M 8067 vim
您的方法不尽如人意,不仅因为 shell 扩展了您的命令替换 ($(...)
) 预先,但更根本的原因是 您无法将 shell 命令行 直接 传递给 find
:
find
的 -exec
操作只能调用 外部实用程序 和 文字 arguments - 唯一支持的非文字参数是 {}
表示手头的文件名。
通过 在每次迭代中调用一个单独的 shell 实例 来解决您的直接问题,要执行的 shell 命令是作为 字符串参数传递 (-exec bash -c '...' \;
).
虽然这是可行的(假设您将 {}
值作为 参数 传递,而不是将其嵌入命令行字符串中),但它也 相当 低效,因为为每个输入文件创建了多个子进程。
(虽然 是 一种让 find
将(通常)所有输入文件传递给(通常) 单个 调用指定的外部实用程序 - 即使用终止符 +
而不是 \;
,这是 而不是 由于传递的命令行的性质,这里是一个选项。 )
高效且稳健[1]
最小化子进程数量的实现创建看起来像这样:
注意:由于使用了 head -n -1
和 sort -h
.[=90=,我假设这里使用 GNU 实用程序]
此外,我将 find
的输出限制为仅 files(与目录相反),因为 wc -l
仅适用于 files.
paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) |
awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", ); print ,,}' |
sort -h -t$'\t' -k1,1
注意使用 -exec ... +
而不是 -exec ... \;
,这确保通常 所有 输入文件名都传递给 对外部实用程序的单个 调用(如果不是所有文件名都适合单个命令行,调用将有效地分批处理以尽可能少地调用)。
wc -l {} +
总是输出摘要行,head -n -1
将其删除,但也会在每行计数后输出文件名。
paste
将每个命令的行(其各自的输入由进程替换提供。<(...)
)组合成单个输出流。
然后 awk
命令从每行的末尾删除源自 wc
的无关文件名。
最后,sort
命令按人类可读的数字(-h
),例如 du -h
输出的数字(例如 1K
)。
[1] 与任何面向 line 的处理一样,不支持嵌入 newlines 的文件名,但是我不认为这是一个现实世界的问题。
我想要一个 bash 命令,它将 return 一个 table,其中每一行都是人类可读的文件大小、行数和文件名。 table 应按文件大小排序。
我一直在尝试结合使用 du -hs
、wc -l
、sort -h
和 find
。
这是我所在的位置:
find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h
问题是您的 shell 解释了 $(...)
,所以 find
没有理解它们。转义它们也无济于事 ($\(du -h {}\)
),因为它们成为命令的正常参数,而不是命令替换。
为了将它们解释为命令替换是调用一个新的shell,或者直接
find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h
或通过创建脚本并从 find
调用它。
好的,我也用 find/-exec 试过了,但是转义很麻烦。使用 shell 函数,它的工作非常简单:
#!/bin/bash
function dir
{
du=$(du -sh "" | awk '{print }')
wc=$(wc -l < "")
printf "%10s %10s %s\n" $du $wc "${1#./}"
}
printf "%10s %10s %s\n" "size" "lines" "name"
OIFS=$IFS; IFS=""
find . -type f -print0 | while read -r -d $'[=10=]' f; do dir "$f"; done
IFS=$OIFS
使用 basishm read 通过使用 nul 终止符甚至是安全的。需要 IFS 来避免读取以截断文件名中的尾随空白。
顺便说一句:$'[=12=]'
并没有真正起作用(与 ''
相同)- 但它使意图明确。
示例输出:
size lines name
156K 708 sash
16K 64 hostname
120K 460 netstat
40K 110 fuser
644K 1555 dir/bash
28K 82 keyctl
2.3M 8067 vim
您的方法不尽如人意,不仅因为 shell 扩展了您的命令替换 ($(...)
) 预先,但更根本的原因是 您无法将 shell 命令行 直接 传递给 find
:
find
的 -exec
操作只能调用 外部实用程序 和 文字 arguments - 唯一支持的非文字参数是 {}
表示手头的文件名。
-exec bash -c '...' \;
).
虽然这是可行的(假设您将 {}
值作为 参数 传递,而不是将其嵌入命令行字符串中),但它也 相当 低效,因为为每个输入文件创建了多个子进程。
(虽然 是 一种让 find
将(通常)所有输入文件传递给(通常) 单个 调用指定的外部实用程序 - 即使用终止符 +
而不是 \;
,这是 而不是 由于传递的命令行的性质,这里是一个选项。 )
高效且稳健[1] 最小化子进程数量的实现创建看起来像这样:
注意:由于使用了 head -n -1
和 sort -h
.[=90=,我假设这里使用 GNU 实用程序]
此外,我将 find
的输出限制为仅 files(与目录相反),因为 wc -l
仅适用于 files.
paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) |
awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", ); print ,,}' |
sort -h -t$'\t' -k1,1
注意使用
-exec ... +
而不是-exec ... \;
,这确保通常 所有 输入文件名都传递给 对外部实用程序的单个 调用(如果不是所有文件名都适合单个命令行,调用将有效地分批处理以尽可能少地调用)。wc -l {} +
总是输出摘要行,head -n -1
将其删除,但也会在每行计数后输出文件名。paste
将每个命令的行(其各自的输入由进程替换提供。<(...)
)组合成单个输出流。然后
awk
命令从每行的末尾删除源自wc
的无关文件名。最后,
sort
命令按人类可读的数字(-h
),例如du -h
输出的数字(例如1K
)。
[1] 与任何面向 line 的处理一样,不支持嵌入 newlines 的文件名,但是我不认为这是一个现实世界的问题。