AWK FPAT 无法按预期进行字符串解析
AWK FPAT not working as expected for string parsing
我必须解析一个非常长的字符串(来自标准输入)。它基本上是一个 .sql 文件。我必须从中获取数据。我正在努力解析数据,以便将其转换为 csv。为此,我正在使用 awk。对于我的情况,示例片段(两条记录)如下:
b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
echo $b|awk 'BEGIN {FPAT = "([^\)]+)|('\''[^'\'']+'\'')"}{print }'
在我的正则表达式中,我说的是在“)”括号上拆分,或者如果找到单引号,则忽略所有文本,直到找到最后一个引号。但是我的输出如下:
(abc@xyz.com,www.example.com,'field2,(2
我期待这个输出
(abc@xyz.com,www.example.com,'field2,(2)'
我的代码哪里出了问题。我搜索了很多并为此查看了 awk 手册但没有成功。
使用您在 GNU awk
中显示的示例编写和测试。这可以在简单的字段分隔符设置中完成,尝试执行一次,其中 b
是您的 shell 变量,其中包含您显示的值。
echo "$b" | awk -F'\),\(' '{print }'
(abc@xyz.com,www.example.com,'field2,(2)'
说明:只需将awk
程序的字段分隔符设置为\),\(
即可输入并先打印它的领域。
我在下面的第一个答案是错误的,您要执行的操作有一个 ERE:
$ echo "$b" | awk -v FPAT="[(]([^)]|'[^']*')*)" '{for (i=1; i<=NF; i++) print $i}'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
原答案,留作另一种方法:
您首先需要一种 2 遍方法,用输入中不存在的内容(例如 RS)替换引用字段中的所有 )
,然后识别 (...)
字段并在打印它们之前将 RS 放回 )
s:
$ echo "$b" |
awk -F"'" -v OFS= '
{
for (i=2; i<=NF; i+=2) {
gsub(/)/,RS,$i)
$i = FS $i FS
}
FPAT = "[(][^)]*)"
[=11=] = [=11=]
for (i=1; i<=NF; i++) {
gsub(RS,")",$i)
print $i
}
FS = FS
}
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
由于 FPAT(或者我们可以使用 gawk patsplit()
),以上内容仅适用于 gawk,对于其他 awk,您使用了 while-match()-substr() 循环:
$ echo "$b" |
awk -F"'" -v OFS= '
{
for (i=2; i<=NF; i+=2) {
gsub(/)/,RS,$i)
$i = FS $i FS
}
while ( match([=12=],/[(][^)]*)/) ) {
field = substr([=12=],RSTART,RLENGTH)
gsub(RS,")",field)
print field
[=12=] = substr([=12=],RSTART+RLENGTH)
}
}
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
与 Ed 所建议的类似的正则表达式方法,但我通常更喜欢使用 RS
和 RT
而不是 FPAT
:
b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
awk -v RS="[(]('[^']*'|[^)])*[)]" 'RT {print RT}' <<< "$b"
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
如果你想接近一次通过,也许试试这个
{mawk/mawk2/gawk} 'BEGIN { OFS = FS = "7"; ORS = RS = "\n";
XFS = "6[=10=]47";
XRS = "1" ORS;
} ! /[1]/ { print; next; } { for (x=1; x <= NF; x += 2) {
gsub(/[1][^0]*/, XFS, $(x)); } } gsub(XFS, XRS) || 1'
我用 2 个 gsub 这样做是为了防止它开始发送下面的行而产生意想不到的后果。 \051 = ")", \050 为空号
- 通过告诉它立即打印并在甚至没有找到右括号的情况下继续前进(所以根本没有什么可分割的)来进一步增强它
它只会在我用单引号 \047 拆分后循环遍历奇数字段(因为偶数字段恰好是一对单引号中的字段,您希望避免在其处砍掉)。
至于 XFS,只需使用几乎不可能遇到的字节来选择您选择的任意组合。如果您想安全起见,可以测试该行中是否存在 XFS,并使用一些替代组合。它基本上是在行的中间插入一个定界符,该定界符不会 运行 与实际输入数据冲突。这本身并不是万无一失的,但 运行 结合 UTF16 字节顺序标记和 ASCII 控制字符的可能性相当低。
(如果您遇到 XFS,很可能您的数据已经损坏,因为 300 系列八进制必须后跟 200 系列八进制才能成为有效的 UTF8)
这样,我就根本不需要 FPAT。
*在最后更新了“|| 1”作为安全包罗万象,但实际上并不需要。
我必须解析一个非常长的字符串(来自标准输入)。它基本上是一个 .sql 文件。我必须从中获取数据。我正在努力解析数据,以便将其转换为 csv。为此,我正在使用 awk。对于我的情况,示例片段(两条记录)如下:
b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
echo $b|awk 'BEGIN {FPAT = "([^\)]+)|('\''[^'\'']+'\'')"}{print }'
在我的正则表达式中,我说的是在“)”括号上拆分,或者如果找到单引号,则忽略所有文本,直到找到最后一个引号。但是我的输出如下:
(abc@xyz.com,www.example.com,'field2,(2
我期待这个输出
(abc@xyz.com,www.example.com,'field2,(2)'
我的代码哪里出了问题。我搜索了很多并为此查看了 awk 手册但没有成功。
使用您在 GNU awk
中显示的示例编写和测试。这可以在简单的字段分隔符设置中完成,尝试执行一次,其中 b
是您的 shell 变量,其中包含您显示的值。
echo "$b" | awk -F'\),\(' '{print }'
(abc@xyz.com,www.example.com,'field2,(2)'
说明:只需将awk
程序的字段分隔符设置为\),\(
即可输入并先打印它的领域。
我在下面的第一个答案是错误的,您要执行的操作有一个 ERE:
$ echo "$b" | awk -v FPAT="[(]([^)]|'[^']*')*)" '{for (i=1; i<=NF; i++) print $i}'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
原答案,留作另一种方法:
您首先需要一种 2 遍方法,用输入中不存在的内容(例如 RS)替换引用字段中的所有 )
,然后识别 (...)
字段并在打印它们之前将 RS 放回 )
s:
$ echo "$b" |
awk -F"'" -v OFS= '
{
for (i=2; i<=NF; i+=2) {
gsub(/)/,RS,$i)
$i = FS $i FS
}
FPAT = "[(][^)]*)"
[=11=] = [=11=]
for (i=1; i<=NF; i++) {
gsub(RS,")",$i)
print $i
}
FS = FS
}
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
由于 FPAT(或者我们可以使用 gawk patsplit()
),以上内容仅适用于 gawk,对于其他 awk,您使用了 while-match()-substr() 循环:
$ echo "$b" |
awk -F"'" -v OFS= '
{
for (i=2; i<=NF; i+=2) {
gsub(/)/,RS,$i)
$i = FS $i FS
}
while ( match([=12=],/[(][^)]*)/) ) {
field = substr([=12=],RSTART,RLENGTH)
gsub(RS,")",field)
print field
[=12=] = substr([=12=],RSTART+RLENGTH)
}
}
'
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
与 Ed 所建议的类似的正则表达式方法,但我通常更喜欢使用 RS
和 RT
而不是 FPAT
:
b="(abc@xyz.com,www.example.com,'field2,(2)'),(dfr@xyz.com,www.example.com,'field0'),"
awk -v RS="[(]('[^']*'|[^)])*[)]" 'RT {print RT}' <<< "$b"
(abc@xyz.com,www.example.com,'field2,(2)')
(dfr@xyz.com,www.example.com,'field0')
如果你想接近一次通过,也许试试这个
{mawk/mawk2/gawk} 'BEGIN { OFS = FS = "7"; ORS = RS = "\n";
XFS = "6[=10=]47";
XRS = "1" ORS;
} ! /[1]/ { print; next; } { for (x=1; x <= NF; x += 2) {
gsub(/[1][^0]*/, XFS, $(x)); } } gsub(XFS, XRS) || 1'
我用 2 个 gsub 这样做是为了防止它开始发送下面的行而产生意想不到的后果。 \051 = ")", \050 为空号
- 通过告诉它立即打印并在甚至没有找到右括号的情况下继续前进(所以根本没有什么可分割的)来进一步增强它
它只会在我用单引号 \047 拆分后循环遍历奇数字段(因为偶数字段恰好是一对单引号中的字段,您希望避免在其处砍掉)。
至于 XFS,只需使用几乎不可能遇到的字节来选择您选择的任意组合。如果您想安全起见,可以测试该行中是否存在 XFS,并使用一些替代组合。它基本上是在行的中间插入一个定界符,该定界符不会 运行 与实际输入数据冲突。这本身并不是万无一失的,但 运行 结合 UTF16 字节顺序标记和 ASCII 控制字符的可能性相当低。
(如果您遇到 XFS,很可能您的数据已经损坏,因为 300 系列八进制必须后跟 200 系列八进制才能成为有效的 UTF8)
这样,我就根本不需要 FPAT。
*在最后更新了“|| 1”作为安全包罗万象,但实际上并不需要。