只有在条件适用的情况下删除非ASCII字符,在bash中

问题描述:

我有一个非常特殊的需求,为此我一直试图解决,但没有成功。只有在条件适用的情况下删除非ASCII字符,在bash中

我有一个日志,它是由一个tcp/ip套接字转储创建的......它将十六进制转换为ASCII,但自然也有一些特殊字符。

我已经设法删除它们,但是我目前遇到了一些困难:有时,发送了一个0x0A,这与我的应用程序混淆了......我试图将其删除,但它也删除了在该行的末尾有效的0x0A ...

基本上,我有,在日志文件:

08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={ 
Teste String2} 
08-14-2017 10:00:00 String={ 
Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

我想最终结果为

08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={Teste String2} 
08-14-2017 10:00:00 String={Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

中的人物总是之间{},所以}之后的每个0x0A都是有效的,但里面不是。

我试过的每一个命令要么删除所有的0x0A,要么根本不工作。

我试过的东西

sed 's/^[^}]*}//' 
sed 's/\x0A$//' 

有什么想法?

+0

你在ASCII文本或十六进制上应用sed命令吗? – pchaigno

+0

关于ASCII文本... –

这当然是可能的SED,但它更容易阅读和理解AWK:

awk 'BEGIN{ OFS=FS="{"; ORS=RS="}" } { sub(/[^[:print:]]/,"",$2) } 1' input.txt 

这是什么呢?

  • 首先,我们设定的输入和输出字段分隔符来{,我们的输入和输出的记录分隔符来}。这让我们可以预测地将括号内的文字作为特定的字段(至少根据您的样本数据)。
  • 接下来,我们用一个空字符串替换字段#2中的所有非打印字符,从而消除换行符,退格等。
  • 最后,我们使用awk速记打印行。

使用sed

的Linux:

$ sed -r ':a;N;$!ba;s/(\{[^}]*)\\n([^{]*\})/\1\2/g' file 
08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={Teste String2} 
08-14-2017 10:00:00 String={Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

的FreeBSD和MacOS:

sed -e ':a' -e 'N;$!ba' -e 's/(\{[^}]*)\\n([^{]*\})/\1\2/g' file 

说明

-e ':a' -e 'N;$!ba'允许我们在sed的每次迭代中考虑当前行和下一行。有关详细信息,请参阅this SO answer

(\{[^}]*)确保有一个开口支架,紧随其后的是一个开口支架。

([^{]*\})正好相反。

+0

在我的FreeBSD或macOS中不起作用。这个GNU-sed是特定的吗? – ghoti

+0

是的,我会更新。 – pchaigno

+0

当你分割它的时候:'sed -E -e':a'-e'N; $!ba'-e's /(\ {[^}] *)\ n([^ {] * \ })/ \ 1 \ 2/g'' ..非GNU sed似乎希望标签后面不要加分号。 – ghoti

的Perl:

$ perl -0777 -pe 's/({[^}]*)\x0A([^}]*})/\1\2/g' file 
08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={Teste String2} 
08-14-2017 10:00:00 String={Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

纯击(基于anubhava的AWK):

while IFS="\n" read -r line; do 
    le="" 
    [[ $line =~ \} ]] && le=$'\n' 
    printf "%s%s" "$line" "$le" 
done <file 

另一个简单awk

awk '{printf "%s%s", $0, (/}/ ? ORS : "")}' file 

08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={Teste String2} 
08-14-2017 10:00:00 String={Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

awk命令检查存在在一行中,然后只打印换行符,否则打印记录不换行。

+1

这太棒了。 – dawg

+1

是的,它应该是'ORS' :) – anubhava

随着GNU AWK多焦RS我们就可以隔离每个{...}串并删除新行内它:

$ awk -v RS='{[^}]+}' '{ORS=gensub(/\n/,"","g",RT)}1' file 
08-14-2017 10:00:00 String={Teste String} 
08-14-2017 10:00:00 String={Teste String2} 
08-14-2017 10:00:00 String={Teste String3} 
08-14-2017 10:00:00 String={Teste String4} 

对于这个特定的情况下,其他的awk答案会工作得很好,上面只是一个更通用的解决方案来隔离分隔字符串,然后在其上执行操作(如在此情况下删除字符)。