如何使用 unix 实用程序将所有转义序列替换为非转义等效项 (sed/tr/awk)
How to replace all escape sequences with non-escaped equivalent with unix utilities (sed/tr/awk)
我正在处理用于显示过滤器的 Wireshark 配置文件 (dfilter_buttons),并希望打印出给定名称的过滤器。文件内容如下:
示例输入
"TRUE","test","sip contains \x22Hello, world\x5cx22\x22",""
并且生成的输出应该替换了转义序列,以便稍后在我的脚本中使用它们:
期望输出
sip contains "Hello, world\x22"
我的第一关是这样的:
当前解析器
filter_name=test
awk -v filter_name="$filter_name" 'BEGIN {FS="\",\""} ( == filter_name) {print }' "$config_file"
我的输出是这样的:
当前输出
sip contains \x22Hello, world\x5cx22\x22
我知道我可以通过管道传输到 sed 并匹配这两个确切的序列来处理这两个确切的转义序列,但是是否有通用的方法来替换所有转义序列?我构建的未来过滤器可能会使用更多的转义序列,而不仅仅是 " 和 ,我想处理未来的情况。
使用 gnu-awk
,您可以使用 split
、gensub
和 strtonum
函数执行此操作:
awk -F '","' -v filt='test' ' == filt {n = split(, subj, /\x[0-9a-fA-F]{2}/, seps); for (i=1; i<n; ++i) printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2)); print subj[i]}' file
sip contains "Hello, world\x22"
更易读的形式:
awk -F '","' -v filt='test' '
== filt {
n = split(, subj, /\x[0-9a-fA-F]{2}/, seps)
for (i=1; i<n; ++i)
printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2))
print subj[i]
}' file
解释:
- 使用
-F '","'
我们使用分隔符 ","
拆分输入
== filt
我们根据 == "test"
条件过滤输入
- 使用
/\x[0-9a-fA-F]{2}/
作为正则表达式(匹配 2 位十六进制字符串)我们拆分 </code> 并将拆分标记保存到数组 <code>subj
并将匹配的分隔符保存到数组 seps
- 我们使用
substr
删除第一个字符,即 \
并添加 0
- 我们使用
strtonum
将十六进制字符串转换为等效的 ascii 数字
- 在
printf
中使用%c
我们打印相应的ascii字符
- 最后一个
for
循环使用 subj
和 seps
数组元素将
连接回去
将 GNU awk 用于 FPAT、gensub()、strtonum() 和第三个参数以匹配 ():
$ cat tst.awk
BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
== ("\"" filter_name "\"") {
gsub(/^"|"$/,"",)
while ( match(,/(\x[0-9a-fA-F]{2})(.*)/,a) ) {
printf "%s%c", substr(,1,RSTART-1), strtonum(gensub(/./,0,1,a[1]))
= a[2]
}
print
}
$ awk -v filter_name='test' -f tst.awk file
sip contains "Hello, world\x22"
以上假定您的转义序列总是 \x
后跟恰好 2 个十六进制数字。它隔离输入中的每个 \xHH
字符串,用 0
替换该字符串中的 \
以便 strtonum()
可以将字符串转换为数字,然后使用 %c
在 printf
格式化字符串中将该数字转换为字符。
请注意,GNU awk 有一个调试器(请参阅 https://www.gnu.org/software/gawk/manual/gawk.html#Debugger),因此如果您不确定程序的任何部分是做什么的,您可以 运行 在调试器中进行调试(-D
) 并追踪它,例如在下面我设置了一个断点来告诉 awk 在脚本的第 1 行停止 (b 1
),然后开始 运行ning (r
) 和步骤 (s
) 通过脚本在每一行打印 $3 (p
) 的值,这样我就可以看到它在 gsub()
:
之后是如何变化的
$ awk -D -v filter_name='test' -f tst.awk file
gawk> b 1
Breakpoint 1 set at file `tst.awk', line 1
gawk> r
Starting program:
Stopping in BEGIN ...
Breakpoint 1, main() at `tst.awk':1
1 BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
gawk> p
= uninitialized field
gawk> s
Stopping in Rule ...
2 == "\"" filter_name "\"" {
gawk> p
= "\"sip contains \x22Hello, world\x5cx22\x22\""
gawk> s
3 gsub(/^"|"$/,"",)
gawk> p
= "\"sip contains \x22Hello, world\x5cx22\x22\""
gawk> s
4 while ( match(,/(\x[0-9a-fA-F]{2})(.*)/,a) ) {
gawk> p
= "sip contains \x22Hello, world\x5cx22\x22"
我正在处理用于显示过滤器的 Wireshark 配置文件 (dfilter_buttons),并希望打印出给定名称的过滤器。文件内容如下:
示例输入
"TRUE","test","sip contains \x22Hello, world\x5cx22\x22",""
并且生成的输出应该替换了转义序列,以便稍后在我的脚本中使用它们:
期望输出
sip contains "Hello, world\x22"
我的第一关是这样的:
当前解析器
filter_name=test
awk -v filter_name="$filter_name" 'BEGIN {FS="\",\""} ( == filter_name) {print }' "$config_file"
我的输出是这样的:
当前输出
sip contains \x22Hello, world\x5cx22\x22
我知道我可以通过管道传输到 sed 并匹配这两个确切的序列来处理这两个确切的转义序列,但是是否有通用的方法来替换所有转义序列?我构建的未来过滤器可能会使用更多的转义序列,而不仅仅是 " 和 ,我想处理未来的情况。
使用 gnu-awk
,您可以使用 split
、gensub
和 strtonum
函数执行此操作:
awk -F '","' -v filt='test' ' == filt {n = split(, subj, /\x[0-9a-fA-F]{2}/, seps); for (i=1; i<n; ++i) printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2)); print subj[i]}' file
sip contains "Hello, world\x22"
更易读的形式:
awk -F '","' -v filt='test' '
== filt {
n = split(, subj, /\x[0-9a-fA-F]{2}/, seps)
for (i=1; i<n; ++i)
printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2))
print subj[i]
}' file
解释:
- 使用
-F '","'
我们使用分隔符","
拆分输入
== filt
我们根据== "test"
条件过滤输入- 使用
/\x[0-9a-fA-F]{2}/
作为正则表达式(匹配 2 位十六进制字符串)我们拆分</code> 并将拆分标记保存到数组 <code>subj
并将匹配的分隔符保存到数组seps
- 我们使用
substr
删除第一个字符,即\
并添加0
- 我们使用
strtonum
将十六进制字符串转换为等效的 ascii 数字 - 在
printf
中使用%c
我们打印相应的ascii字符 - 最后一个
for
循环使用subj
和seps
数组元素将连接回去
将 GNU awk 用于 FPAT、gensub()、strtonum() 和第三个参数以匹配 ():
$ cat tst.awk
BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
== ("\"" filter_name "\"") {
gsub(/^"|"$/,"",)
while ( match(,/(\x[0-9a-fA-F]{2})(.*)/,a) ) {
printf "%s%c", substr(,1,RSTART-1), strtonum(gensub(/./,0,1,a[1]))
= a[2]
}
print
}
$ awk -v filter_name='test' -f tst.awk file
sip contains "Hello, world\x22"
以上假定您的转义序列总是 \x
后跟恰好 2 个十六进制数字。它隔离输入中的每个 \xHH
字符串,用 0
替换该字符串中的 \
以便 strtonum()
可以将字符串转换为数字,然后使用 %c
在 printf
格式化字符串中将该数字转换为字符。
请注意,GNU awk 有一个调试器(请参阅 https://www.gnu.org/software/gawk/manual/gawk.html#Debugger),因此如果您不确定程序的任何部分是做什么的,您可以 运行 在调试器中进行调试(-D
) 并追踪它,例如在下面我设置了一个断点来告诉 awk 在脚本的第 1 行停止 (b 1
),然后开始 运行ning (r
) 和步骤 (s
) 通过脚本在每一行打印 $3 (p
) 的值,这样我就可以看到它在 gsub()
:
$ awk -D -v filter_name='test' -f tst.awk file
gawk> b 1
Breakpoint 1 set at file `tst.awk', line 1
gawk> r
Starting program:
Stopping in BEGIN ...
Breakpoint 1, main() at `tst.awk':1
1 BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
gawk> p
= uninitialized field
gawk> s
Stopping in Rule ...
2 == "\"" filter_name "\"" {
gawk> p
= "\"sip contains \x22Hello, world\x5cx22\x22\""
gawk> s
3 gsub(/^"|"$/,"",)
gawk> p
= "\"sip contains \x22Hello, world\x5cx22\x22\""
gawk> s
4 while ( match(,/(\x[0-9a-fA-F]{2})(.*)/,a) ) {
gawk> p
= "sip contains \x22Hello, world\x5cx22\x22"