【问题标题】:Comparison of cat pipe awk operation to awk command on a filecat pipe awk 操作与文件上的 awk 命令的比较
【发布时间】:2015-03-30 07:29:57
【问题描述】:

在尝试优化一些与服务器相关的数据时,我和我的团队讨论了 linux 命令的使用。会请求会员帮助我们更准确地理解这个概念。

在服务器上,我们有每分钟创建的日志文件,我们需要搜索带有特定标签的日志,例如:错误日志、超时日志、请求失败日志。在众多标签中,一项要求是提供有关这些标签数量的信息

简单的逻辑是 awk 特定字段(带分隔符)进行排序,使用 uniq -c 命令计算此类实例的数量。

我可以看到两种执行方式:

cat fname | awk -F":" {'print $1'} | sort | uniq -c

awk -F":" {'print $1'} fname | sort | uniq -c

文件大小可以以 GB 为单位,因此哪个命令可能更有效。

【问题讨论】:

  • 为什么不grep "TAG" log.txt | wc -l
  • Awk 非常适合字段操作,但对于简单的计数,我会使用 grep。
  • grep 的问题是它会计算标签(如果在其他字段中可用)。例如:“超时标签”的描述为“尝试连接 IP 时出错”。所以我们可能会得到错误的计数。

标签: linux bash shell awk


【解决方案1】:

有 3 种方法可以打开文件并让 awk 对其内容进行操作:

  1. cat 打开文件:

    cat file | awk '...'
    
  2. shell 重定向打开文件:

    awk '...' < file
    
  3. awk 打开文件

    awk '...' file
    

在这些选择中:

  1. 总是要避免的,因为cat 和管道正在使用资源并且没有提供任何价值,请谷歌 UUOC(无用使用 Cat)了解详细信息。

另外两个使用哪个是有争议的:

  1. 具有外壳打开文件而不是工具的优点,因此如果您对所有工具执行此操作,您可以依赖一致的错误处理
  2. 的优点是该工具知道它正在操作的文件的名称(例如 awk 中的 FILENAME),因此您可以在内部使用它。

要查看差异,请考虑以下两个文件:

$ ls -l file1 file2
-rw-r--r-- 1 Ed None 4 Mar 30 09:55 file1
--w------- 1 Ed None 0 Mar 30 09:55 file2
$ cat file1
a
b
$ cat file2
cat: file2: Permission denied

看看当您尝试使用两种打开方法对两者的内容运行 awk 时会发生什么:

$ awk '{print FILENAME, $0}' < file1
- a
- b

$ awk '{print FILENAME, $0}' file1
file1 a
file1 b

$ awk '{print FILENAME, $0}' < file2
-bash: file2: Permission denied

$ awk '{print FILENAME, $0}' file2
awk: fatal: cannot open file `file2' for reading (Permission denied)

请注意,当您使用重定向时,打开不可读文件 file2 的错误消息来自 shell,因此看起来与我第一次尝试 cat 时的错误消息完全一样,而让 awk 打开它时的错误消息来自 awk,与 shell 消息不同,并且在不同的 awk 中会有所不同。

请注意,当使用 awk 打开文件时,FILENAME 填充了正在操作的文件的名称,但当使用重定向打开文件时,它被设置为 -

我个人认为“3”(填充的文件名)的好处远远超过“2”(文件打开错误的一致错误处理)的好处,所以我会一直使用:

awk '...' file

对于你会使用的特定问题:

awk -F':' '{cnt[$1]++} END{for (i in cnt) print cnt[i], i}' fname

【讨论】:

    【解决方案2】:

    绝对无用的cat 应该避免使用:

    awk -F":" '{print $1}' fname | sort | uniq -c
    

    但我的建议是避免昂贵的 sortuniq 命令,方法是使用以下方法在 awk 本身中查找唯一项目:

    awk -F":" '!seen[$1]++' fname
    

    这将打印唯一的行。

    获取唯一计数:

    awk -F":" '!count[$1]++{c++} END{print c}' fname
    

    【讨论】:

    • 一个更简单的选择是awk -F":" '!seen[$1]++' fname
    • 当您使用名为 seen 的数组时,其目的是仅对密钥的第一个实例与所有其他实例做不同的事情,因此您应该将名称更改为 count 或类似名称。此外,正如您所写的,您的第二个脚本只会打印每个唯一键两次。
    • 感谢@EdMorton,是的,我有额外的END 块,现在已编辑。
    【解决方案3】:

    cat fname | 稍微减慢了速度,因为它必须将文件从磁盘复制到内核,然后复制到 cat 的缓冲区,然后复制到管道,管道再次进入内核,然后再复制到另一个进程。这不是很多,因为它应该只是线性减速并且内存中复制非常快,但是您可以总是(=不依赖于some_commands 接受文件参数)速度通过替换来解决问题

    cat one_file_name | some_command 
    

    <one_file_name some_command
    

    这样会更快,因为它将直接将 one_file_name 设置为 stdin of some_command

    &lt;one_file_name 可以并且经常放置在之后 some_command 和下一个管道符号之前。我个人经常喜欢从它开始,因为它反映了从左到右的无用但有点普遍使用 cat (cat one_file_name) 的流程。

    【讨论】:

    • 您关于使用重定向而不是cat 的观点是正确的,但它忽略了许多工具接受文件名作为参数的事实,因此不需要重定向。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2016-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多