【问题标题】:Add a prefix to logs with AWK使用 AWK 为日志添加前缀
【发布时间】:2018-11-28 16:29:45
【问题描述】:

我遇到了需要用于日志分析的脚本的问题;让我解释一下这个问题:

我有一个像这样的 gzip 文件:

5555_prova.log.gz

文件里面有这样的mali日志行:

2018-06-12    03:34:31    95.245.15.135    GET    /hls.playready.vod.mediasetpremium/farmunica/2018/06/218742_163f10da04c7d2/hlsrc/w12/21.ts

我需要一个脚本来读取 gzip 压缩的日志文件,该文件能够在标准输出上输出修改后的日志行,如下所示:

5555 2018-06-12    03:34:31    95.245.15.135    GET    /hls.playready.vod.mediasetpremium/farmunica/2018/06/218742_163f10da04c7d2/hlsrc/w12/21.ts

如您所见,日志行现在以从 gzip 文件名中读取的数字开始。 我需要这条新线路来提供 logstash 数据处理链。

我试过这样的脚本:

 echo "./5555_prova.log.gz" | xargs -ISTR -t -r  sh -c "gunzip -c STR | awk '{$0="5555 "$0}' "

这不是我所需要的(前缀是静态的,不是用文件名中的正则表达式捕获的)但即使使用这个简化版本,我也会收到错误:

sh -c gunzip -c ./5555_prova.log.gz | awk '{-bash=5555 -bash}'
-bash}' : -c: line 0: unexpected EOF while looking for matching `''
-bash}' : -c: line 1: syntax error: unexpected end of file

从上面的输出中可以看出,$0 不再是通过管道传递给 awk 的整行,而是一个奇怪的-bash

我需要使用 xargs,因为 gzip 压缩文件的列表是从另一个工具(即实例化的 inotifywait 监听通过 ftp 写入文件的目录)提供给命令行的。 我错过了什么?你有什么建议可以为我指明正确的方向吗?

问候, S.


尝试遵循@Charles Duffy 的建议,我编写了这段代码:

#/bin/bash

#
# Usage: sendToLogstash.sh [pattern]
#
# Executes a command whenever files matching the pattern are closed in write
# mode or moved to. "{}" in the command is replaced with the matching filename (via xargs).
# Requires inotifywait from inotify-tools.
#
# For example,
#
#    whenever.sh '/usr/local/myfiles/'
#
#


DIR="$1"
PATTERN="\.gz$"

script=$(cat <<'EOF'
awk -v filename="$file" 'BEGIN{split(filename,array,"_")}{$0=array[1] OFS $0} 1' < $(gunzip -dc "$DIR/$file")
EOF
)

inotifywait -q --format '%f' -m -r -e close_write -e moved_to "$DIR" \
      | grep --line-buffered $PATTERN | xargs -I{} -r sh -c "file={}; $script"

但我得到了错误:

[root@ms-felogstash ~]# ./test.sh ./poppo
gzip: /1111_test.log.gz: No such file or directory
gzip: /1111_test.log.gz: No such file or directory
sh: $(gunzip -dc "$DIR/$file"): ambiguous redirect

感谢您的帮助,我在编写 bash 脚本时感到很失落。

问候, S.

【问题讨论】:

  • 你有嵌套引号,bash 不明白。它认为您的 "gunzip... 引用以 '{$0=" 结尾并且因为您有 1 个单引号而感到困惑。
  • 您的内部单引号是文字,而不是句法,因此在将内容传递给 awk 之前,它们不会阻止 $0 被 shell 替换。 (当由 bash 而不是 awk 解释时,$0 按照惯例/默认是当前脚本的名称;对于交互式解释器,该名称是 -bash

标签: bash awk xargs


【解决方案1】:

永远不要使用xargs -I 将字符串替换为sh -c(或bash -c 或将该字符串解释为代码的任何其他上下文)。这允许恶意文件名运行任意命令 - 想想如果有人运行 touch $'$(rm -rf ~)\'$(rm -rf ~)\'.gz' 并将该文件放入您的日志会发生什么。

相反,让xargs 在您的脚本文本之后 附加参数,并编写脚本以将这些参数作为数据进行迭代/读取,而不是将它们替换为代码。


为了展示如何安全地使用xargs(好吧,安全地如果我们假设您已经过滤掉了带有文字换行符的文件名):

# This way you don't need to escape the quotes in your script by hand
script=$(cat <<'EOF'
for arg; do gunzip -c <"$arg" | awk '{$0="5555 "$0}'; done
EOF
)

# if you **did** want to escape them by hand, it would look like this:
#   script='for arg; do gunzip -c <"$arg" | awk '"'"'{$0="5555 "$0}'"'"'; done'

echo "./5555_prova.log.gz" | xargs -d $'\n' sh -c "$script" _

为了更安全地使用所有可能的文件名,您可以改用:

printf '%s\0' "./5555_prova.log.gz" | xargs -0 sh -c "$script" _

注意使用 NUL 分隔的输入(使用 printf '%s\0' 创建)和 xargs -0 来使用它。

【讨论】:

  • 我需要实际的错误才能说什么。如果它不完全适合评论,您能否在gist.github.com 发布您尝试的确切代码和错误?
  • 抱歉,我正在写评论,消息未完成。我会将代码添加到我的消息中..
  • 您编辑的代码明显违背了我在此处的建议,我建议您在任何情况下都不要将文件名替换为解析为代码的字符串。 file={} 正是这种替换。
  • 另外,当你的代码包含&lt;(...)之类的东西时,它们是bash语法,你需要使用bash -c,而不是sh -c
  • 一个不太不正确的实现看起来像gist.github.com/charles-dyfis-net/…。另请注意,我们必须 export dir 才能被 bash -csh -c 调用的子进程看到。
【解决方案2】:

编辑:另外,如果您正在处理多个 .gz 文件并想要打印它们的内容以及它们的文件名(第一列 _ 分隔)然后跟随可以帮到你。

for file in *.gz; do
    awk -v filename="$file" 'BEGIN{split(filename,array,"_")}{$0=array[1] OFS $0} 1' <(gzip -dc "$file")
done


我还没有测试你的代码(也不能完全理解),所以在这里尝试提供一种方式,以防你的代码可以将文件名传递给awk,那么附加文件的第一个将非常简单数字如下(只是一个例子)。

awk 'FNR==1{split(FILENAME,array,"_")} {$0=array[1] OFS $0} 1' 5555_prova.log_file

所以在这里,我将 FILENAME 用于 awk 的开箱即用变量(仅在文件的第一行中),然后将其拆分为名为 array 的数组,然后将其添加到文件的每一行中。

还要将 "gunzip -c STR 包装为结尾 ",在您将其输出传递给 awk 之前,它似乎已丢失。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 2013-09-02
    • 1970-01-01
    • 2022-07-22
    • 2015-05-13
    • 1970-01-01
    相关资源
    最近更新 更多