regex - Bash 使用正则表达式将 API 调用的结果添加到日志文件
问题描述
我有这种格式的原始数据:
[Wed Sep 01 15:37:10.709437 2021] [authz_core:error] [pid 15732] [client [IP REDACTED]:56163] AH01630: client denied by server configuration: /var/www/[REDACTED].com/html/wp-admin/admin-post.php
API 调用如下所示:
curl 'https://api.ipgeolocation.io/ipgeo?apiKey=[REDACTED]&ip=[REDACTED]'
结果是:
{"ip":"[REDACTED]","continent_code":"EU","continent_name":"Europe","country_code2":"DE","country_code3":"DEU","country_name":"Germany","country_capital":"Berlin","state_prov":"Bayern","district":"Oberbayern","city":"München","zipcode":"81549","latitude":"[REDACTED]","longitude":"[REDACTED]","is_eu":true,"calling_code":"+49","country_tld":".de","languages":"de","country_flag":"https://ipgeolocation.io/static/flags/de_64.png","geoname_id":"8772423","isp":"[REDACTED]","connection_type":"","organization":"[REDACTED]","currency":{"code":"EUR","name":"Euro","symbol":"€"},"time_zone":{"name":"Europe/Berlin","offset":1,"current_time":"2021-09-02 23:08:55.514+0200","current_time_unix":1630616935.514,"is_dst":true,"dst_savings":1}}
到目前为止我得到了什么:
grep -P "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]+\b" ~/Documents/hosting/web/logscript_results.txt
这匹配任何 IPv4 地址和端口号。我希望将端口替换为相应地址的 country_name,例如:
[Wed Sep 01 15:37:10.709437 2021] [authz_core:error] [pid 15732] [client [IP REDACTED]: Germany AH01630: client denied by server configuration: /var/www/[REDACTED].com/html/wp-admin/admin-post.php
如何从我的正则表达式继续前进?
解决方案
正如评论中提到的,如果您要在 bash 中使用 JSON 并且能够在系统上安装软件包,那么您绝对应该获取并使用jq
.
首先,让我们grep
获取 IP 地址的原始数据/日志文件,并使用命令替换将其存储在变量中。在这里,我正在使用您的正则表达式,但添加了-o
仅捕获 IP+端口的选项:
LOG_IP_PORT=$(grep -Po "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]+\b" ~/Documents/hosting/web/logscript_results.txt)
现在,我假设 ipgeolocation API 只需要 IP 地址,而不需要端口。即使它可以接受端口,将 IP 地址仅存储在另一个变量中也会对我们有用。所以我们将使用变量替换来修剪字符串:
LOG_IP_ONLY=${LOG_IP_PORT/%:*}
接下来,是时候使用 curl 进行 API 调用了,并使用我们的$LOG_IP_ONLY
变量代替 IP 地址查询字段。同样,我们将结果存储在带有命令替换的变量中:
JSON_RESPONSE=$(curl "https://api.ipgeolocation.io/ipgeo?apiKey=[REDACTED]&ip=${LOG_IP_ONLY}")
# must use double-quotes ("), NOT single-quotes (')
下面是jq
解析 API 响应的地方:
COUNTRY_NAME=$(jq -r '.country_name' <<< $JSON_RESPONSE)
# the -r option outputs only raw data without any quotation
但是,如果您必须使用grep
/regex 而不是jq
,请划掉最后一行并使用:
COUNTRY_NAME=$(grep -Po '"country_name"\s*:\s*"\K([^"]*)' <<< $JSON_RESPONSE)
现在我们有了所有必要的数据,我们只需要在您的日志文件中进行实际替换:
sed -i "s/${LOG_IP_PORT}/${LOG_IP_ONLY}:${COUNTRY_NAME}/g" ~/Documents/hosting/web/logscript_results.txt
# the -i option edits the input file in place
把它们放在一起:
#!/bin/bash
LOG_IP_PORT=$(grep -Po "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]+\b" ~/Documents/hosting/web/logscript_results.txt)
LOG_IP_ONLY=${LOG_IP_PORT/%:*}
JSON_RESPONSE=$(curl "https://api.ipgeolocation.io/ipgeo?apiKey=[REDACTED]&ip=${LOG_IP_ONLY}")
COUNTRY_NAME=$(jq -r '.country_name' <<< $JSON_RESPONSE)
# or:
# COUNTRY_NAME=$(grep -Po '"country_name"\s*:\s*"\K([^"]*)' <<< $JSON_RESPONSE)
sed -i "s/${LOG_IP_PORT}/${LOG_IP_ONLY}:${COUNTRY_NAME}/g" ~/Documents/hosting/web/logscript_results.txt
需要注意的是,只有在您的日志文件中只有一个 IP 地址时,这才会按原样工作。
如果您只想将此应用于您可以使用的日志文件中的第一个 IP 地址head
,如下所示:
LOG_IP_PORT=$(grep -Po "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]+\b" ~/Documents/hosting/web/logscript_results.txt | head -n 1)
如果您只想将此应用于日志文件中的最后一个 IP 地址,请使用tail
:
LOG_IP_PORT=$(grep -Po "\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):[0-9]+\b" ~/Documents/hosting/web/logscript_results.txt | tail -n 1)
如果要将此脚本应用于日志文件中的所有 IP 地址,则需要定义 IP 地址数组并使用for
.
推荐阅读
- excel - 按空格或选择更多单元格时出错:“运行时错误 13,类型:不匹配”
- python - clearsceeen 和 equal 函数在我的计算器代码中没有响应
- java - Java 代码在逐步调试模式下运行良好,而不是在默认运行模式下
- .net - 在没有安装 excel 客户端的情况下从 .net 代码生成 excel 文件
- log4j2 - 每个 JUnit 5 测试的日志文件,以便将其附加到 Allure 报告?
- python - 如何在python代码中拥有cfiles
- asp.net-core - ASP.NET Core 2.2 如何在支持HTTPS PFX证书的同时绑定域名?
- ruby - 无法在 Heroku 服务器上以生产模式运行
- python - 如何通过 Python 中的命名空间对象访问 argparse 位置参数
- javascript - React Firebase 中多个数据之间的信息