我有一个 CSV 文件,我花了一天的大部分时间来处理它,但我没有运气使用 awk 的正则表达式正确解析它。
awk 没有按预期处理正则表达式。
以下是输入:
- GNU Awk 4.1.4,API:1.1(GNU MPFR 3.1.5-p2,GNU MP 6.1.2)
- 正则表达式:
/(\[(.*?)\])|[^,]+/g
- 示例文本
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1, role2, role3],[recipe1, recipe2, recipe3],2019-01-10 06:06:31
- 原始文本(在删除双引号之前,我在这个问题中未明确列出的步骤中执行此操作):
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,"[""role1"", ""role2"", ""role3""]","[""recipe1"", ""recipe2"", ""recipe3""]",2019-01-10 06:06:31
当我运行这个时正则表达式网站,它显示正确的匹配:
我从 cat -> sed -> awk 进行管道传输(上面的示例文本是 sed 中的内容)并运行以下命令(我只需要前 9 个字段,其中包括 [] 中包含的第一个字段的全部内容,但是之后什么都没有):
awk '/(\[(.*?)\])|[^,]+/g{print $1,$2,$3,$4,$5,$6,$7,$8,$9}'
我期望看到的输出:
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1, role2, role3]
笔记:关于此的重要部分是将具有角色的字段(括号之间)视为单个字段,或者至少包括输出中的所有角色,但不包括任何配方)
我实际上得到的是输入的完整线路。
通过使用变量,我发现 awk 中出现了以下字段分配:
- 1 美元 =
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1,
- $2 =
role2,
- 3 美元=
role3],[recipe1,
- 4 美元=
recipe2,
- 5 美元=
recipe3],2019-01-10
- 6 美元=
06:06:31
我尝试使用已接受的答案这个堆栈溢出问题直接,我尝试调整它以使用 [] 作为分隔符而不是 ",这让我更接近,但它仍然没有将角色字段视为单个字段。
答案1
如果您正在处理一个复杂的 CSV 文件 - 特别是其字段可能包含引号分隔符(在本例中为逗号)的文件,那么正确的 CSV 解析器将节省很多麻烦,例如csvtool
$ echo 'hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,"[""role1"", ""role2"", ""role3""]","[""recipe1"", ""recipe2"", ""recipe3""]",2019-01-10 06:06:31' |
csvtool col 1-9 -
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,"[""role1"", ""role2"", ""role3""]"
或(删除引号)
$ echo 'hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,"[""role1"", ""role2"", ""role3""]","[""recipe1"", ""recipe2"", ""recipe3""]",2019-01-10 06:06:31' |
csvtool col 1-9 - | tr -d '"'
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1, role2, role3]
如果您无法获得独立的 CSV 解析器(例如 )csvtool
,那么 Perl 和 Python 都有 CSV 模块,例如
perl -MText::CSV -lpe '
BEGIN{$p = Text::CSV->new()}
$_ = join ",", map { $_ = s/"//gr } ($p->fields())[0..8] if $p->parse($_)
'
答案2
默认情况下,awk
将使用空格来定义字段,这解释了为什么您会获得所看到的输出。由于您想使用逗号来分隔字段,因此您需要使用-F
:
awk -F, '{...}'
要awk
打印逗号分隔的输出,您需要设置OFS
变量:
awk -F, -vOFS=, '{...}'
这里真正的困难是你试图将其视为[role1, role2, role3]
单个字段,但那是 3 个字段。那里有逗号,所以会被分成[role1
,role2
和role3]
。如果您知道那里总是有 3 个字段,那就很简单:
$ awk -F, -vOFS=, '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11}' file
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1, role2, role3]
但是,根据您现在添加的原始数据,正确的 CSV 解析器永远是更好的方法,你仍然可以在awk
.只需在原始输入数据上运行:
$ awk -F']' -vOFS=, '{gsub(/"/,"");print $1"]"}' file
hostname,hostname.domain.com,hostname.domain.com,windows,6.2.9200,1.2.3,location,environment,[role1, role2, role3]
技巧是用作]
字段分隔符并告诉awk
仅打印第一个字段。这将打印直到第一个的所有内容]
。然后我们添加回]
(因为在构建字段时它被删除)。删除gsub
所有引号。