【问题标题】:Bash count total number of files and lines in loopBash 计算循环中文件和行的总数
【发布时间】:2021-07-12 11:46:16
【问题描述】:

我正在尝试计算我执行脚本的目录中每个 .xls 的文件和行数。

total_files=0
total_lines=0

find . -type f -name "*.xls" | while read FILE; do

 count=$(grep -c ^ < "$FILE") #get number of lines in particular file
 total_lines=$(($total_lines+$count));
 ((total_files++))

done

echo "Total files: $total_files"
echo "Total lines: $total_lines"

但我每次只得到0。

我知道这可能是因为 while 循环是在子 shell 中执行的, 但使用&lt;&lt;&lt; 在当前shell 中执行不会有帮助:

done <<< "$(find . -type f -name "*.xls")"

【问题讨论】:

  • *.xls,这是一个 Excel 文件吗? Excel 文件中的行数是什么意思?
  • 在您的一个文件上运行 file foo.xls。如果输出没有告诉您它是一个文本文件,那么不要费心尝试在其上运行文本处理工具。同样,查看head foo.xls 的输出并确定它对您来说是否像纯文本。
  • Excel 文件不是 POSIX 文本文件,因此您无法运行像 grep 这样的在其上运行的文本文件并期望获得有意义的输出的工具。您需要找到能够理解 Excel 文件的内容来告诉您它包含多少行,或者将所有 Excel 文件导出为 CSV,然后在这些文件上运行 Unix 工具。有关可能的工具/方法,请参阅 stackoverflow.com/q/38805123/1745001 和/或 unix.stackexchange.com/q/23726/133219
  • 是的,它的输出并不可靠,如果你在任何时候都能得到正确的输出,那真是令人震惊。我刚刚创建了一个 3 行的 XLS,grep -c ^ &lt; "$D/tst.xls" 告诉我它包含 11,874 行。
  • @EdMorton 哇,谢谢,这真的很有帮助

标签: linux bash awk sed


【解决方案1】:

Unix 行计数方法严格适用于 TEXT 文件,包括(大部分)csv 文件。

Excel 文件不是文本。 Excel files 是直到 2007 年使用的 .xls 类型的二进制文件,或者是此后使用的 .xlsx 打开 xml 样式文件。两者都不是面向线的。

唯一可靠的方法是使用可以解析 Excel 文件并可以遍历文件树的脚本语言。

Python、RubyPerl 在 Unix 上都可以做到这一点。

这是一个带有 Pandas 的 Python,用于遍历文件树、读取文件并计算该文件默认工作表的行数:

# Note: You may need to use pip to install
#       pandas
#       xldd
#       openpyxl

import pandas as pd
from pathlib import Path 

p=Path('ur_root_path')

file_count=0
line_count=0
for pn in p.glob('**/*.xls*'):
    try:
        df=pd.read_excel(pn,header=None)
        print(f'{pn}\n{df}')
        file_count+=1
        line_count+=df.shape[0]
    except ValueError as e:
        print(f'{pn}: {e}')
    
print(f'files={file_count}, total lines={line_count}')  

鉴于此文件夹:

$ ls -1 /tmp/test
Book1.xlsx          # 3 rows in col 'A' -- 1-3
Book2.xls           # 6 rows in col 'C' -- 1-6

运行该脚本会产生:

/tmp/test/Book2.xls
    0   1  2
0 NaN NaN  1
1 NaN NaN  2
2 NaN NaN  3
3 NaN NaN  4
4 NaN NaN  5
5 NaN NaN  6
/tmp/test/Book1.xlsx
   0
0  1
1  2
2  3
files=2, total lines=9

【讨论】:

    【解决方案2】:

    工作得很好:

    #!/usr/bin/env bash
    
    total_files=0
    total_lines=0
    
    while IFS= read -r file; do
      count="$(wc -l < "$file")"
      (( total_lines+=count ))
      (( total_files++ ))
      echo "$count"
    done <<< "$(find . -type f -name '*.xls')"
    

    【讨论】:

    • 需要明确的是,虽然这适用于 POSIX 文本文件,但它不适用于 .xls 文件,因为它不是 POSIX 文本文件。请参阅问题下的 cmets。
    【解决方案3】:

    awk 是完成这项任务的更好工具,因为它可以在一次运行中本地完成这两个计数:

    find . -type f -iname '*.xls' -exec awk \
     'END{printf("Number of files: %d\nTotal number of lines: %d\n", ARGC, NR)}' {} \+
    

    【讨论】:

    • 这仍然会失败,因为输入不是文本文件,你不能依赖+ 一次性提供awk 的所有文件,不像;,你不需要'不需要转义+,输入文件的数量是ARGC-1,因为第一个arg是解释器的名字。
    【解决方案4】:

    这可能对你有用:

    find . -type f -name "*xls"  2>/dev/null|xargs -n1 wc |sed 's/^/1 /'|numsum -cx1,2
    

    查找文件,使用 wc 处理每个文件,使用 sed 预先添加一个文件计数器列,并使用来自 num-utilsnumsum 计算前两列。

    输出格式为总:文件和行、单词和字符可以通过将numsum-x命令行选项更改为-x1,2,3,4来添加。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-25
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多