2016-08-13 63 views
1

我想要一个将返回一个表的bash命令,其中每行是可读的文件大小,行数和文件名。该表应按文件大小排序。人类可读的文件大小和行数

我一直在努力做到这一点使用的du -hswc -lsort -hfind的组合。

下面是我在哪里:

find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h 
+0

告诉我们你得到了什么 – eckes

+0

@eckes用非工作代码编辑 – Hatshepsut

回答

1

你的做法短期下跌不仅是因为外壳扩展您的命令替换($(...)前面,但更根本的,因为你不能传递外壳命令行直接find

find-exec动作只能调用外部公用事业字面参数 - 支持的唯一非文字形参是表示手头的文件名(多个){}

choroba's answer修复您通过眼前的问题在每次迭代中调用一个单独的壳例如,为了外壳命令来执行作为字符串参数-exec bash -c '...' \;)被传递哪个。
虽然这工作(假设你通过{}值作为参数而不是在命令行字符串嵌入它),它也是相当低效,因为多个子进程创建每个输入文件

(虽然办法有find通(典型地)所有输入文件到一个(典型地)调用指定的外部工具的 - 即与终止子+而非\;,这是这里由于传递的命令行的性质的选项。)

一种高效且健壮的[1] 执行该孩子的数目最小化处理创建应该是这样的:

注:我假设这里GNU公用事业,由于使用的head -n -1sort -h
另外,我限制find的输出文件只(而不是目录),因为wc -l仅适用于文件

paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) | 
    awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", $3); print $1,$2,$3}' | 
    sort -h -t$'\t' -k1,1 
  • 使用注意事项的-exec ... +而非-exec ... \;,这确保了通常所有输入的文件名被传送到单个调用到外部实用程序(如果不是所有的文件名适合于单一命令行,调用有效批处理以尽可能少的调用)。

  • wc -l {} +总是输出一条摘要行,其中head -n -1去掉,但也在每行数后输出文件名。

  • paste组合来自每个命令(其各自的输入端通过取代。<(...)的处理被设置)成单个输出流的行。

  • awk命令然后从每行的末尾去除源自wc的外部文件名。

  • 最后,sort命令由第一排序结果(-k1,1)制表符分隔(-t$'\t')柱通过人类可读的数字(-h),如数字,du -h输出(例如,1K)。


[1]与任何线取向处理,嵌入式换行符文件名不支持,但我不认为这是一个现实世界的问题。

0

的问题是,你的shell解释$(...),所以find没有得到他们。转义它们对(\$\(du -h {}\))无效,因为它们成为命令的正常参数,而不是命令替换。

为了它们解释为命令替换是调用一个新的外壳,无论是直接

find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h 

,或者通过创建脚本,并从find调用它。

+0

是否有可能只显示文件名一次? – Hatshepsut

+1

@Hatshepsut:当然,使用'wc -l <​​{}'。 – choroba

+0

不幸的是,这将打破文件名与嵌入式元字符,如空格(并可能与嵌入式globbing字符)。 要解决这个问题,你必须使用'find。 -exec bash -c'echo'$(du -h“$ 1”)$(wc -l <​​“$ 1”)“' - {} \; |然而,即使这解决了OP的直接问题并且非常短,但这种方法对于大型输入文件集将执行得不好,因为它会创建多个子进程_per filename_。 – mklement0

1

好吧,我也试过用find/-exec,但逃跑是地狱。具有外壳功能,它的工作原理非常简单:

#!/bin/bash 
function dir 
{ 
    du=$(du -sh "$1" | awk '{print $1}') 
    wc=$(wc -l < "$1") 
    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 $'\0' f; do dir "$f"; done 
IFS=$OIFS 

使用basishm阅读它甚至可以使用nul终止符来保证安全。 IFS需要避免读取来截断文件名中的尾随空白。

顺便说一下:$'\0'实际上并不奏效(与''相同) - 但它使意图清晰。

输出示例:

 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