创建包含文件名的新文件并计算每个文件

Create new file containing filename and count each file

我需要创建一个包含文件名和行数的新 file_count.txt

目录结构

$ find asia emea -name \*.gz   
asia/2013/emp_asia_13.txt.gz  
asia/2015/emp_asia_15.txt.gz  
asia/2014/emp_asia_14.txt.gz  
emea/2013/emp_emea_13.txt.gz  
emea/2015/emp_emea_15.txt.gz  
emea/2014/emp_emea_14.txt.gz

输出文件应该是这样的:

emp_asia_13.txt.gz 20  
emp_asia_15.txt.gz 15  
emp_asia_14.txt.gz 50  
emp_emea_13.txt.gz 32  
emp_emea_15.txt.gz 26  
emp_emea_14.txt.gz 70

解决方案使用 for 循环

for file in $(find asia emea -name \*.gz -print0 | xargs -0)
do
    echo -n $(basename $file);
    gunzip -c $file |wc -l;
done >> file_count.txt

在一行中,它给出:

$ for file in $(find asia emea -name \*.gz -print0 | xargs -0); do echo -n $(basename $file); gunzip -c $file |wc -l; done >> file_count.txt

输出为:

$ cat file_count.txt
emp_asia_13.txt.gz       4
emp_asia_14.txt.gz      10
emp_emea_15.txt.gz      17

您也可以试试:

find asia emea -type f -name "*gz" | while IFS= read -r fname; do
    printf "%s %s\n" "$fname" $(gzip -dc "$fname" | wc -l) >> file_count.txt
done

作为 1-liner 将是:

find asia emea -type f -name "*gz" | while IFS= read -r fname; do printf "%s %s\n" "$fname" $(gzip -dc "$fname" | wc -l) >> file_count.txt; done

为了 运行 shell 在 find 的结果上添加一些不破坏任何特殊字符的方式,您可以使用 find -exec sh -c ...。 (见下文)。

在这种情况下,如果您可以使用 bash 的 extglob 为您匹配子目录,则您并不需要它。我刚刚意识到这是一个 ksh 问题,如果它有等同的东西,我就知道了。

shopt -s extglob
for i in {asia,emea}/**/*.gz;do
    bn=${i##*/}  # basename
    printf "%s %s\n" "$bn"  "$(zcat "$i"|wc -l)"   # stolen from David's answer
done > linecounts.txt  # redirect once outside the loop.

这类似于 David 的回答,只是即使在名称包含换行符的文件中它也能成功计算行数。但是,输出文件会很乱,因为换行符是文本数据的常用记录分隔符,所以在文件名中使用它只是自​​找麻烦。

如果您知道自己的目录结构,则不需要 extglob,只需使用 */*/*.gz。可选地使用一些前导字符来切断一些子目录搜索。 (bash 在遍历目录时也不像 find 那样聪明。它总是 stat 看它是否是一个目录,即使是在 d_type 字段中填写 readdir(3) 结果。)

请注意,对于 extglob,您 需要 dir/**/*.gz,而不仅仅是 dir/**.gz


更一般地说,您可以通过使用 xargs 运行 sh -c,然后在 [=25] 中将 findxargs 和 shell 命令一起使用=], 遍历位置参数。 for i 隐含地这样做;即它等同于 for i in "$@".

find -name '*.gz` -print0 | xargs -0 bash -c 'for i in "$@";do ...loop body from above...;done > linecounts.txt' bash

你可以将其简化为 find 运行 sh -c 本身,如果你有 find 支持 + 终结符 -exec(将匹配列表放入一个命令行):

find -name '*.gz` -exec bash -c 'for i in "$@";do ...loop body from above...;done > linecounts.txt' bash {} +

在这两种情况下,你需要在来自 findxargs 的 args 之前有一个虚拟 arg,因为它最终会成为 argv[0] (传统上是命令名称)。