如何改进这个问题,要么参数太低,要么 for 循环太慢

如何改进这个问题,要么参数太低,要么 for 循环太慢

当我尝试时

du -sh $(sed -ne '/\.[hc]$/p' ../all-file)

它说 $ bash: /usr/bin/du: Argument list too long

然后我尝试

for i in $(sed -ne '/\.[hc]$/p' ../all-file); do du "$i"; done|vim -

但需要很长时间才能完成。

我怎样才能加快速度而不用大惊小怪?

答案1

您是否已经根据提供的评论弄清楚了这一点?

sed -ne '/\.[hc]$/p' ../all-file | xargs du -sh

虽然我质疑为什么不使用 grep ?速度至少快两倍。

grep '\.[hc]$' ../all-file | xargs du -sh

答案2

通过 GNU 实现du,您可以执行以下操作:

<../all-file LC_ALL=C grep '\.[hc]$' |
  tr '\n' '\0' |
  du -sh --files0-from=-

通过 GNU 实现xargs

<../all-file LC_ALL=C grep '\.[hc]$' |
  xargs -rd '\n' du -sh --

或者使用一些xargs复制了 GNU 的其他实现-r-0如果不是的话,-d比如在大多数 BSD 上:

<../all-file LC_ALL=C grep '\.[hc]$' |
  tr '\n' '\0' |
  xargs -r0 du -sh --

但请注意,由于du仅报告一次硬链接的磁盘使用情况,因此如果您像这样将列表分成几批xargs,则最终可能会得到不同的结果。如果您想使用该-c选项在最后获得累积结果,您还必须使用该--files0-from方法。

例子:

$ seq 100000 > a.c
$ ln a.c b.c
$ du -shc a.c b.c
651K    a.c
651K    total

查看如何b.c不报告,因为这实际上与a.c.两个文件的累计磁盘使用量为 651K。

$ du -shc a.c; du -shc  b.c
651K    a.c
651K    total
651K    b.c
651K    total

您得到 2 个总计 651K 的文件,这隐藏了 ac 和 bc 是同一个文件的事实。

另一方面,如果您想禁用硬链接处理,并通过 GNU 实现来单独报告每个文件的磁盘使用情况,du则可以使用-l/--count-links选项。--apparent-size如果您对文件的大小而不是其磁盘使用情况感兴趣,另请参阅该选项。

在现代版本的 Linux 上,您可以通过提高资源限制来提高参数 + 环境的大小限制stacksize

例如,使用:ulimit -s unlimited在大多数 shell 中(也在limit stacksize unlimitedzsh 中,它仅适用于子进程),您可能能够超越限制并避免分解列表。

$ /bin/true {1..150000}
zsh: argument list too long: /bin/true
(127)$ limit stacksize unlimited
$ /bin/true {1..150000}
$ /bin/true {1..250000}
$ /bin/true {1..350000}
$ /bin/true {1..500000}
zsh: argument list too long: /bin/true

然后,您也许可以使用 split+glob,但与往常一样,您需要对其进行调整以将分隔符修复为仅换行符 ( IFS=$'\n') 并禁用您可能不想要的通配符 ( set -o noglob):

(
  ulimit -s unlimited
  IFS=$'\n'
  set -o noglob
  du -sh -- $(<../all-file LC_ALL=C grep '\.[hc]$')
)

../all-file需要注意的是,在没有与该模式匹配的行的情况下,您最终将du在不带参数的情况下运行,这将默认获取当前工作目录的磁盘使用情况(可以-r选择xargs处理这些情况)。

还有一些注意事项:

  • sed -ne /re/pgrep(这是grep缩写词,虽然来自eds g/re/p)。
  • 文件路径可以由任何非 NUL 字节组成,为了能够表示它们的任意列表,一个明显的选择是用 NUL 分隔它们,因此--files0-from需要 NUL 分隔的列表。这意味着您的换行符分隔all-file文件无法列出任意文件路径。这也意味着,由于文件路径不必由文本组成,因此不能保证使用文本实用程序处理它们能够正常工作,除非在 C 等所有字节都是字符的语言环境中。使用LC_ALL=C也可能会提高性能,因为它避免了将字节解码为字符。
  • $(...)bash 中不加引号的是 split+glob。如果您知道文件路径不包含全局字符也不包含$IFS变量中的字符,则只能使用该文件列表来拆分文件列表。
  • 对于xargs没有-0/ 的-d情况,拆分是在空格或换行符上完成的,单/双引号和反斜杠被解释为转义运算符,这意味着它无法处理换行符分隔的任意文件路径列表,就像您all-file看起来的那样。
  • 当将变量、预先未知的参数列表传递给命令时,您需要确保--在它之前使用它来标记选项的结尾,以确保第一个(或任何具有 GNU 实现的)参数以-(或+对于某些命令),它不被视为一个选项。

相关内容