2017-06-30 46 views
5

我有一個日誌文件(來自客戶)。 18 Gigs。該文件的所有內容都在1行中。 我想讀取logstash中的文件。但是我因爲記憶而出現問題。該文件逐行讀取,但不幸的是它全部在1行上。用一個巨大的(12GB)由1行代替}

我試圖通過用}\n替換文件分成行,以便logstash可以處理它(該文件有一個簡單的JSON格式,沒有嵌套對象)我想有在一行中,分裂各JSON在}

sed -i 's/}/}\n/g' NonPROD.log.backup 

sed死亡 - 我也假設因爲記憶。我該如何解決這個問題?我可以讓sed使用其他數據塊而不是行來處理文件嗎?我知道默認sed逐行讀取。

+2

您可能要考慮使用'jq'來以流方式處理您的文件。 – chepner

回答

6

以下僅使用內置於殼的功能:

#!/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

,你可以:

  1. 分割文件說1M塊使用split -b 1m file.log
  2. 過程中的所有文件sed 's/}/}\n/g' x*
  3. ...和重定向的sed輸出到回相結合,單件

這樣做的缺點是雙倍的存儲空間。

+1

無論如何將輸出序列化到磁盤,執行的變換都會生成大於原始輸入的輸出,而無論如何,存儲將會翻倍 - 而'sed -i'正在寫入臨時輸出,輸出需要去*某處*。只有當輸出大小相等或者更小時,才能進行真正的就地轉換。如果您在「split」完成後立即刪除了原始文件,則可以避免超過double + 1mb(加上額外的換行符本身的大小)。 –

+0

(...好 - 「只」有點強--Linux有一些擴展,允許在內存映射文件中的特定塊被追溯刪除,如果使用適當的文件系統,它會變得稀疏)。 –

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架。)

+0

很簡單,但它起作用(除了在文件末尾插入一個額外的'}',如果沒有的話)。我建議把'cat'帶出管道 - '

+0

厭倦了 - 時間減少了1%。 – Jack

+1

是的。如果程序類似於'sort',它將會是一個更大的區別,它可以在給定可查找文件句柄時並行化,而不需要用FIFO前後讀取。 ('cat file |'不提供對'file'的直接訪問,但僅限於來自'cat'的流輸出)。另一個例子是'wc -c' - 無論文件有多大,它都可以在一個固定的時間內完成一個真正的文件句柄,並且使用FIFO來無條件地讀取整個文件。 –

0

fold

$ fold -w 1000 long_line_file | sed 's/}/}\n\n/g' | tr -s '\n' 
+0

你在不需要它們的地方換行。 –

3

另一種選擇用GNU AWK爲RT

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

與其他awks:

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

我決定測試所有當前發佈的解決方案用於使用輸入文件的功能和執行時間此命令生成E:

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 

GOT預期的輸出,定時=

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

3)tr+sed

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

沒有產生預期的輸出(由不期望的}在文件的結尾),定時=

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' 

GOT預期的輸出,定時=

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