AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 436823
Accepted
Siva
Siva
Asked: 2018-04-11 09:06:48 +0800 CST2018-04-11 09:06:48 +0800 CST 2018-04-11 09:06:48 +0800 CST

并行脚本进程

  • 772

我想解析apache访问日志wrt IP。我使用了以下代码,但花了将近 90 秒。

grep "^$CLIENT_IP" /var/log/http/access.log > /tmp/access-$CLIENT_IP.log

然后我尝试了如下替代方法。

sed -i -e "/^$CLIENT_IP/w /tmp/access-$CLIENT_IP.log" -e '//d' /var/log/http/access.log

甚至这也花了 60 多秒。

有 1200 个 IP 需要解析。我想知道有什么方法可以实现并行性以减少运行时间。

shell-script shell
  • 3 3 个回答
  • 146 Views

3 个回答

  • Voted
  1. Best Answer
    Kusalananda
    2018-04-11T11:46:42+08:002018-04-11T11:46:42+08:00

    我假设您在所有 IP 地址的 shell 循环中执行此操作,可能 IP 地址来自文本文件。是的,这会很慢,一次调用sed或grep每个 IP 地址。

    sed相反,如果您仔细准备,您可能会一次使用.

    首先,我们必须创建一个sed脚本,我们从一个ip.list包含 IP 地址的文件中执行此操作,每行一个地址:

    sed -e 'h' \
        -e 's/\./\\./g' \
        -e 's#.*#/^&[[:blank:]]/w /tmp/access-#' \
        -e 'G' \
        -e 's/\n//' \
        -e 's/$/.log/' ip.list >ip.sed
    

    这个sed东西,对于每个IP地址,

    1. 将地址复制到“保留空间”(中的一个额外缓冲区sed)。
    2. 将“模式空间”(输入行)更改.为\.(为了正确匹配点,您的代码没有这样做)。
    3. 前置^和附加[[:blank:]]/w /tmp/access-到模式空间。
    4. 将未修改的输入行从保持空间附加到模式空间,中间有一个换行符。
    5. 删除该换行符。
    6. 追加.log到行尾(并隐式输出结果)。

    对于包含

    127.0.0.1
    10.0.0.1
    10.0.0.100
    

    这将创建sed脚本

    /^127\.0\.0\.1[[:blank:]]/w /tmp/access-127.0.0.1.log
    /^10\.0\.0\.1[[:blank:]]/w /tmp/access-10.0.0.1.log
    /^10\.0\.0\.100[[:blank:]]/w /tmp/access-10.0.0.100.log
    

    请注意,您必须在 IP 地址后匹配一个空白字符(空格或制表符),否则日志条目10.0.0.100将进入/tmp/access-10.0.0.1.log文件。您的代码省略了这一点。

    然后可以在您的日志文件上使用它(无循环):

    sed -n -f ip.sed /var/log/http/access.log
    

    我从未测试过从同一个sed脚本写入 1200 个文件。如果它不起作用,请尝试以下awk变体。


    一个类似的解决方案awk涉及首先将 IP 地址读入一个数组,然后将它们与每一行进行匹配。这需要一次awk调用:

    awk 'FNR == NR  { list[$1] = 1; next }
         $1 in list { name = $1 ".log"; print >>name; close name }' ip.list /var/log/http/access.log
    

    在这里,我们awk同时给出 IP 列表和日志文件。当NR == FNR我们知道我们仍在读取第一个文件(列表)时,我们将 IP 编号list作为键添加到关联数组中,然后继续下一行输入。

    如果FNR == NR条件不成立,我们将从第二个文件(日志文件)中读取,并测试输入行的第一个字段是否是一个键list(这是一个纯字符串比较,而不是正则表达式匹配) . 如果是,我们将该行附加到适当命名的文件中。

    我们必须小心关闭输出文件,否则我们可能会用完打开的文件描述符。所以会有很多打开和关闭文件用于追加,但它仍然比awk每个 IP 地址调用(或任何实用程序)一次要快。


    我很想知道这些东西是否适合您以及大概的运行时间可能是多少。我只在极小的数据集上测试了这些解决方案。


    grep当然,我们可以接受您的想法,即通过在系统上并行抛出 eg 的多个实例来强制它:

    忽略我们没有正确匹配 IP 地址中的点的事实,我们可能会像这样

    xargs -P 4 -n 100 sh -c '
        for n do
            grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
        done' sh <ip.list
    

    在这里,xargs一次最多将 100 个 IP 地址从ip.list文件提供给一个简短的 shell 脚本。它将安排脚本的四个并行调用。

    简短的 shell 脚本:

    for n do
        grep "^$n[[:blank:]]" /var/log/http/access.log >"/tmp/access-$n.log"
    done
    

    这只会遍历xargs在其命令行上提供它的 100 个 IP 地址,并应用与您拥有的几乎相同的grep命令,不同之处在于将有四个这样的循环并行运行。

    增加或-P 4与-P 16您拥有的 CPU 数量相关。加速可能不是线性的,因为每个并行实例grep都会从同一个磁盘读取和写入。

    除了-P标志 to xargs,这个答案中的所有东西都应该能够在任何 POSIX 系统上运行。for的-P标志xargs是非标准的,但在 GNUxargs和 BSD 系统中实现。

    • 3
  2. Dark Matter
    2018-04-11T09:37:32+08:002018-04-11T09:37:32+08:00

    对于各种方法: https ://stackoverflow.com/questions/9066609/fastest-possible-grep

    除此之外,如果您经常这样做,那么 SSD 可能是您的最佳选择。触摸高清是此类事情的杀手锏。

    您有大量不同的 grep 需要运行。制作一个脚本,将脚本命令(例如,每个核心一个)启动到后台,然后跟踪它们何时完成,因为它们已经完成了更多的启动。

    当我这样做时,我可以让所有 12 个内核以 100% 的 CPU 使用率运行,但你可能会发现你的资源限制是别的东西。鉴于您的所有工作都需要相同的文件,如果您不在 SSD 上,您可能希望复制该文件,以免它们共享。

    • 1
  3. Ole Tange
    2018-04-16T14:49:29+08:002018-04-16T14:49:29+08:00

    如果/var/log/http/access.log大于 RAM 并因此无法缓存,那么并行运行更多进程可能是access.log多次读取的一个很好的替代方案 - 特别是如果您有多个内核。这将为grep每个 IP 并行运行一个(+ 几个帮助包装过程)。

    pargrep() {
        # Send standard input to grep with different match strings in parallel
        # This command would be enough if you only have 250 match strings
        parallel --pipe --tee grep ^{} '>' /tmp/access-{}.log ::: "$@"
    }
    export -f pargrep
    # Standard input is tee'ed to several pargreps.
    # Each pargrep gets 250 match strings and thus starts 250 processes.
    # For 1200 ips this starts 3600 processes taking around 1 GB RAM,
    # but it reads access.log only once
    cat /var/log/http/access.log |
      parallel --pipe --tee -N250 pargrep {} :::: ips
    
    • 0

相关问题

  • 打印文件行及其长度的脚本[关闭]

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 按分隔符拆分并连接字符串问题

  • 如何将带有〜的路径保存到变量中?

  • MySQL Select with function IN () with bash array

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve