用一个由1行组成的巨大(12GB)中的} \ n替换每个}?

时间:2017-06-30 17:06:28

标签: bash logging sed logstash

我有一个日志文件(来自客户)。 18演出。该文件的所有内容都在一行。 我想在logstash中读取该文件。但是因为记忆我得到了问题。该文件逐行读取,但遗憾的是它全部在1行。

我尝试将文件拆分成行,以便logstash可以处理它(文件有一个简单的json格式,没有嵌套对象)我想让每个json在一行中,在}分割,替换为{ {1}}:

}\n

但是sed -i 's/}/}\n/g' NonPROD.log.backup 被杀了 - 我还认为也是因为记忆。我该如何解决这个问题?我可以让sed使用除行之外的其他数据块来操作文件吗?我知道默认情况下sed会逐行读取。

5 个答案:

答案 0 :(得分:6)

以下仅使用内置于shell中的功能:

#!/bin/bash

# as long as there exists another } in the file, read up to it...
while IFS= read -r -d '}' piece; do
  # ...and print that content followed by '}' and a newline.
  printf '%s}\n' "$piece"
done

# print any trailing content after the last }
[[ $piece ]] && printf '%s\n' "$piece"

如果您已将logstash配置为从TCP端口读取(使用14321作为下面的任意示例),您可以运行thescript <NonPROD.log.backup >"/dev/tcp/127.0.0.1/14321"或类似的,并且您可以 - 无需双原始输入文件在磁盘上的可用空间,因为到目前为止需要的其他答案。

答案 1 :(得分:3)

使用RT的GNU awk:

$ printf 'abc}def}ghi\n' | awk -v RS='}' '{ORS=(RT?"}\n":"")}1'
abc}
def}
ghi

与其他问题:

$ printf 'abc}def}ghi\n' | awk -v RS='}' -v ORS='}\n' 'NR>1{print p} {p=$0} END{printf "%s",p}'
abc}
def}
ghi

我决定使用此命令生成的输入文件测试所有当前发布的功能和执行时间的解决方案:

awk 'BEGIN{for(i=1;i<=1000000;i++)printf "foo}"; print "foo"}' > file1m

这就是我得到的:

1)awk(上面的两个awk脚本都有类似的结果):

time awk -v RS='}' '{ORS=(RT?"}\n":"")}1' file1m

得到预期的输出,时间=

real    0m0.608s
user    0m0.561s
sys     0m0.045s

2)shell loop

$ cat tst.sh
#!/bin/bash

# as long as there exists another } in the file, read up to it...
while IFS= read -r -d '}' piece; do
  # ...and print that content followed by '}' and a newline.
  printf '%s}\n' "$piece"
done

# print any trailing content after the last }
[[ $piece ]] && printf '%s\n' "$piece"

$ time ./tst.sh < file1m

得到预期的输出,时间=

real    1m52.152s
user    1m18.233s
sys     0m32.604s

3)tr+sed

$ time tr '}' '\n' < file1m | sed 's/$/}/'

未产生预期的输出(在文件末尾添加了不受欢迎的}),timing =

real    0m0.577s
user    0m0.468s
sys     0m0.078s

通过调整来删除最终不受欢迎的}

$ time tr '}' '\n' < file1m | sed 's/$/}/; $s/}//'

real    0m0.718s
user    0m0.670s
sys     0m0.108s

4)fold+sed+tr

$ time fold -w 1000 file1m | sed 's/}/}\n\n/g' | tr -s '\n'

得到预期的输出,时间=

real    0m0.811s
user    0m1.137s
sys     0m0.076s

5)split+sed+cat

$ cat tst2.sh
mkdir tmp$$
pwd="$(pwd)"
cd "tmp$$"
split -b 1m "${pwd}/${1}"
sed -i 's/}/}\n/g' x*
cat x*
rm -f x*
cd "$pwd"
rmdir tmp$$

$ time ./tst2.sh file1m

得到预期的输出,时间=

real    0m0.983s
user    0m0.685s
sys     0m0.167s

答案 2 :(得分:2)

您可以通过tr运行它,然后在每行末尾重新放置结束括号:

$ cat NonPROD.log.backup | tr '}' '\n' | sed 's/$/}/' > tmp$$
$ wc -l NonPROD.log.backup tmp$$
    0 NonPROD.log.backup
   43 tmp10528
   43 total

(我的测试文件只有43个括号。)

答案 3 :(得分:1)

你可以:

  1. 使用split -b 1m file.log
  2. 将文件拆分为1M块
  3. 处理所有文件sed 's/}/}\n/g' x*
  4. ...并重定向sed的输出以将它们组合回单件
  5. 这样做的缺点是存储空间增加了一倍。

答案 4 :(得分:0)

fold

的另一种选择
$ fold -w 1000 long_line_file | sed 's/}/}\n\n/g' | tr -s '\n'