Awk 将字段分隔符括在引号中

Awk 将字段分隔符括在引号中,awk,Awk,我们使用oracle的sqlldr加载由第三方创建的数据文件。数据文件大小不同,有些文件非常大 数据文件的字段由分隔符分隔| 示例:字段1 |字段2 |字段3 |字段4 |字段5 字段3可以是: 空的 单值 值1 |值2 |值3 如果字段3包含|,我需要用引号将其括起来 我已经制作了一个shell脚本来实现这一点,但是它有点慢——处理一个47000000行的文件大约需要16分钟 我想用awk来做,但我对语法不太熟悉,而且最后期限不允许学习/开发/调试 在awk中是否会明显更快 有简单的方法吗 感

我们使用oracle的sqlldr加载由第三方创建的数据文件。数据文件大小不同,有些文件非常大

数据文件的字段由分隔符分隔|

示例:字段1 |字段2 |字段3 |字段4 |字段5

字段3可以是:

空的 单值 值1 |值2 |值3 如果字段3包含|,我需要用引号将其括起来

我已经制作了一个shell脚本来实现这一点,但是它有点慢——处理一个47000000行的文件大约需要16分钟

我想用awk来做,但我对语法不太熟悉,而且最后期限不允许学习/开发/调试

在awk中是否会明显更快

有简单的方法吗

感谢

给予:

$ cat file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|value1|value2|field4|field5
field1|field2|value1|value2|value3|field4|field5
您可以使用此awk:

awk  '  BEGIN{FS=OFS="|"}        # sep fields on |
        NF<=5{print; next}       # if there are <=5, we are done with line
        {s=$1 OFS $2 OFS "\""    # form first 2 fields + "
        # now loop through the extra fields adding to string after quote:
        for (i=3;i<=NF-5+3;i++) s=(i<NF-5+3) ? s $i OFS : s $i
        s=s "\"" OFS $(NF-5+4) OFS $(NF)   # finish the string
        print s                            # then print it
        }' file 
是的,这将大大加快与awk相比,只是外壳

如果您想要一个单行程序,我会这样使用perl:

perl -F'[|]' -lpE  's/^([^|]+\|[^|]+\|)(.*)(\|[^|]+\|[^|]+)$/\1"\2"\3/ if scalar @F!=5' file
相同的输出。

给定:

$ cat file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|value1|value2|field4|field5
field1|field2|value1|value2|value3|field4|field5
您可以使用此awk:

awk  '  BEGIN{FS=OFS="|"}        # sep fields on |
        NF<=5{print; next}       # if there are <=5, we are done with line
        {s=$1 OFS $2 OFS "\""    # form first 2 fields + "
        # now loop through the extra fields adding to string after quote:
        for (i=3;i<=NF-5+3;i++) s=(i<NF-5+3) ? s $i OFS : s $i
        s=s "\"" OFS $(NF-5+4) OFS $(NF)   # finish the string
        print s                            # then print it
        }' file 
是的,这将大大加快与awk相比,只是外壳

如果您想要一个单行程序,我会这样使用perl:

perl -F'[|]' -lpE  's/^([^|]+\|[^|]+\|)(.*)(\|[^|]+\|[^|]+)$/\1"\2"\3/ if scalar @F!=5' file

相同的输出。

使用Perl one liner,在47e6行上运行约1.5分钟:

perl -F'[|]' -lane '@first = splice @F, 0, 2; @last = splice @F, -2, 2; print join "|", @first, ( @F > 1 ? q{"} . ( join "|", @F ) . q{"} : @F ), @last;' in_file
输入:

field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|val1|val2|val3|field4|field5
输出:

field1|field2|"field3"|field4|field5
field1|field2|""|field4|field5
field1|field2|"val1|val2|val3"|field4|field5
Perl one liner使用以下命令行标志: -e:告诉Perl在线查找代码,而不是在文件中。 -n:一次循环输入一行,默认情况下将其分配给$。 -l:在行内执行代码之前,默认情况下在*NIX上去掉\n输入行分隔符,并在打印时附加它。 -a:将$拆分为空格上的数组@F或-F选项中指定的正则表达式上的数组@F。 -F'/[|]/':在|上拆分为@F,而不是空格

另见:

基准:


平均运行时间为1分钟31秒。使用为darwin-thread-multi-2level构建的perl 5,版本30,subversion 3 v5.30.3,在MacBook Pro,macOS 10.14.6上运行,测量了3次。

使用此perl单行程序,在47e6行上运行约1.5分钟:

perl -F'[|]' -lane '@first = splice @F, 0, 2; @last = splice @F, -2, 2; print join "|", @first, ( @F > 1 ? q{"} . ( join "|", @F ) . q{"} : @F ), @last;' in_file
输入:

field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|val1|val2|val3|field4|field5
输出:

field1|field2|"field3"|field4|field5
field1|field2|""|field4|field5
field1|field2|"val1|val2|val3"|field4|field5
Perl one liner使用以下命令行标志: -e:告诉Perl在线查找代码,而不是在文件中。 -n:一次循环输入一行,默认情况下将其分配给$。 -l:在行内执行代码之前,默认情况下在*NIX上去掉\n输入行分隔符,并在打印时附加它。 -a:将$拆分为空格上的数组@F或-F选项中指定的正则表达式上的数组@F。 -F'/[|]/':在|上拆分为@F,而不是空格

另见:

基准:


平均运行时间为1分钟31秒。使用perl 5,30版,subversion 3 v5.30.3,为darwin-thread-multi-2level构建,在MacBook Pro,macOS 10.14.6上运行,测量了3次。

在每个Unix机箱的任何shell中使用任何awk:

$ awk -F'|' 'NF>5{sub(/^([^|]*\|){2}/,"&\""); sub(/(\|[^|]*){2}$/,"\"&")} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
备选方案:

使用具有-E的sed启用ERE,例如GNU和BSD/OSX sed:

$ sed -E 's/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})/\1"\3"\4/' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
对于任何POSIX sed:

$ sed 's/^\(\([^|]*|\)\{2\}\)\(.*|.*\)\(\(|[^|]*\)\{2\}\)/\1"\3"\4/' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
GESUB的GNU awk:

$ awk '{$0=gensub(/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})$/,"\\1\"\\3\"\\4",1)} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
第三个参数要匹配的GNU awk:

$ awk 'match($0,/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})$/,a){$0=a[1] "\"" a[3] "\"" a[4]} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
以上是针对示例输入文件运行的:


在每个Unix设备上的任意shell中使用任意awk:

$ awk -F'|' 'NF>5{sub(/^([^|]*\|){2}/,"&\""); sub(/(\|[^|]*){2}$/,"\"&")} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
备选方案:

使用具有-E的sed启用ERE,例如GNU和BSD/OSX sed:

$ sed -E 's/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})/\1"\3"\4/' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
对于任何POSIX sed:

$ sed 's/^\(\([^|]*|\)\{2\}\)\(.*|.*\)\(\(|[^|]*\)\{2\}\)/\1"\3"\4/' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
GESUB的GNU awk:

$ awk '{$0=gensub(/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})$/,"\\1\"\\3\"\\4",1)} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
第三个参数要匹配的GNU awk:

$ awk 'match($0,/^(([^|]*\|){2})(.*\|.*)((\|[^|]*){2})$/,a){$0=a[1] "\"" a[3] "\"" a[4]} 1' file
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
以上是针对示例输入文件运行的:

另一个awk

$ cat philb2
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|value1|value2|field4|field5
field1|field2|value1|value2|value3|field4|field5
$ awk -F"|" ' NF==5{print; next} {OFS="|"; v1=$(NF);v2=$(NF-1);f1=$1;f2=$2;$1=$2=""; m=substr($0,3,length($0)-length(v1 v2)-4); print f1,f2,"\"" m "\"",v2,v1; } ' philb2
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
$
另一个awk

$ cat philb2
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|value1|value2|field4|field5
field1|field2|value1|value2|value3|field4|field5
$ awk -F"|" ' NF==5{print; next} {OFS="|"; v1=$(NF);v2=$(NF-1);f1=$1;f2=$2;$1=$2=""; m=substr($0,3,length($0)-length(v1 v2)-4); print f1,f2,"\"" m "\"",v2,v1; } ' philb2
field1|field2|field3|field4|field5
field1|field2||field4|field5
field1|field2|"value1|value2"|field4|field5
field1|field2|"value1|value2|value3"|field4|field5
$

根据输入:a | b | c | d | e | f | g,你如何判断第三个字段是c还是c | d | e?field1 | field2 | value1 | value2 | value3 | field4 | field5?很抱歉,我忘了提到字段2和字段2之间有字段,因此字段2和字段2之间有字段2field4@dawg对准确地给出输入:a | b | c | d | e | f | g,你如何判断第三个字段是c还是c | d | e?那么field1 | field2 | value1 | value3 | field4 | field5变成field1 | field2 | value1 | value2 | value3 | field4 | field5?@Williampersell很抱歉,我忘了提到字段2和Field3之间有字段field4@dawg对没错哇!看起来很棒-我今晚上班时会看的。谢谢我对此进行了测试,结果发现awk而不是perl是唯一有效的解决方案。其他一些方法也很有效,除了在所有第三个字段上都加上双引号,不管是否有额外的|。非常感谢@dawg!!!感谢大家出人意料的快速反应!现在我将花一周左右的时间试图了解awk是如何工作的;o@philb我发布的解决方案没有在所有第三个字段周围加双引号。“你能告诉我什么方式不起作用吗?”埃德·莫顿(ed morton)抱歉——我的评论含糊不清。我说了一些,但不是你的。你的也行。我将此标记为解决方案,因为不知何故,我只是在看到此答案后才看到您的答案…哇!看起来很棒-我今晚上班时会看的。谢谢我对此进行了测试,结果发现awk而不是perl是唯一有效的解决方案。除t外,其他一些也起了作用
嘿,不管是否有额外的|,在所有第三个字段上都加上双引号。非常感谢@dawg!!!感谢大家出人意料的快速反应!现在我将花一周左右的时间试图了解awk是如何工作的;o@philb我发布的解决方案没有在所有第三个字段周围加双引号。“你能告诉我什么方式不起作用吗?”埃德·莫顿(ed morton)抱歉——我的评论含糊不清。我说了一些,但不是你的。你的也行。我将此标记为解决方案,因为不知何故,我只是在看到此答案后才看到您的答案…谢谢,但如果字段3中没有分隔符,则不应将其括在引号中。@philb。。我刚刚更新了答案。。请您检查一下。没有工作对不起-没有对格式语法进行疯狂的格式化语法。以下以下是您可以检查一下。没有工作工作对不起-没有对格式语法的格式化语法进行疯狂的。没有工作工作对不起-没有对格式语法进行疯狂的格式化。以下以下以下是您可以检查。没有工作工作对不起-没有工作对不起-没有工作对不起-工作对不起-工作对不起-工作对不起-对不起-对不起-对不起-对不起-对不起-对不起-没有对对格式语法语法语法语法语法语法的。没有工作:工作:现场1。工作:现场1。工作:现场1 4 4 4 4。工作:现场1;现场1;现场1;现场2现场2;现场3现场3现场3现场3现场3现场4现场4 124;现场4四四四四四124;现场4现场3现场3现场3现场3现场4现场3现场4现场4 1244;现场3现场3现场3现场3现场4现场3现场3现场3现场3 1244 1244;现场3;现场1现场1现场1现场1现场1;字段5field1 | field2 | | field4 | field5 | field2 | field4 | field1 | field2 | value1 | value2 | field4 | field1 | value1 | value2 | value3 | field4 | field5 | philb..抱歉..搞砸了。。你能查一下我的最新情况吗。。如果可以的话。。我将在回答中仅保留这一点谢谢,但是如果字段3中没有分隔符,则不应使用引号。@philb。。我刚刚更新了答案。。请您检查一下。没有工作对不起-没有对格式语法进行疯狂的格式化语法。以下以下是您可以检查一下。没有工作工作对不起-没有对格式语法的格式化语法进行疯狂的。没有工作工作对不起-没有对格式语法进行疯狂的格式化。以下以下以下是您可以检查。没有工作工作对不起-没有工作对不起-没有工作对不起-工作对不起-工作对不起-工作对不起-对不起-对不起-对不起-对不起-对不起-对不起-没有对对格式语法语法语法语法语法语法的。没有工作:工作:现场1。工作:现场1。工作:现场1 4 4 4 4。工作:现场1;现场1;现场1;现场2现场2;现场3现场3现场3现场3现场3现场4现场4 124;现场4四四四四四124;现场4现场3现场3现场3现场3现场4现场3现场4现场4 1244;现场3现场3现场3现场3现场4现场3现场3现场3现场3 1244 1244;现场3;现场1现场1现场1现场1现场1;字段5field1 | field2 | | field4 | field5 | field2 | field4 | field1 | field2 | value1 | value2 | field4 | field1 | value1 | value2 | value3 | field4 | field5 | philb..抱歉..搞砸了。。你能查一下我的最新情况吗。。如果可以的话。。我将在回答中单独保留这一点