我需要通读所有文件并查找<双引号>(“),然后将整行复制到另一个文件中。这里的挑战是,当其中有一个新字符时,要标识整行线。
文件格式是这样的-值用定界符|*|
分隔,并以|##|
结尾。
在所附的(图像)中,绿色突出显示的内容应转到新文件,逻辑将检查"
,如果发现从(从#到|| ## |到下一个|#)开始的读取行。 #|)
10338|*|BVL-O-G-01020-R4|*||*|BVL|*||*|Y|*|Y|*||*|CFC6E82284990A7AE040800AA5644B19|*|jmorlan|*|2011.12.21 15:52:01|##|
10358|*|BI-MED-CDMA-MCS-90-118-EXAM|*|Exam for 001-MCS-90-118:
Planning, Conducting and Reporting Post Marketing Surveillance "Studies and Safety Reporting from Non Trial Activities |*|GLOBAL_MEDICAL|*||*|Y|*|N|*||*|CFC6E822849A0A7AE040800AA5644B19|*|finke|*|2012.04.30 04:23:27|##|
10342|*|BVL-O-4-01020-R7|*||*|DVL|*||*|Y|*|Y|*||*|RRFC6E82284990A7AE040800AA5644B19|*|sppa|*|2011.12.21 15:52:01|##|
答案 0 :(得分:0)
假设您的意思是|##|
之间的部分应视为换行符,那么下一个问题是您的文件中是否包含任何实际的换行符?如果不是这样,grep
可能会逐行运行,因此效率可能不高。如果应该将任何真实的换行符视为文本的一部分,那么毫无疑问,grep会感到不高兴。
如果您真的想一遍就做,请输入grep:
grep -Eoz'(^ | \ | ## \ |)([^ |] | \ | [^#] | \ |#[^#] | \ | ## [^ |]) “([[^ |] | \ | [^#] | \ |#[^#] | \ | ## [^ |])(\ | ## \ || $)'
这正在寻找以| ## |开头的任何序列。 (或文件的开头)后跟一些字符,一个引号和其他一些字符,然后以| ## |结尾。 (或文件末尾)。通过使用-z grep,将忽略文件中的任何换行符。
复杂的“任何字符” ([^|]|\|[^#]|\|#[^#]|\|##[^|])*
表达式是因为grep贪婪。基本上,它寻找不是| ## |的重复序列。也许关闭贪婪是件好事,但这取决于您的grep版本中正则表达式引擎的功能。
但是使用sed分解记录并注入“ NULL”换行符要容易得多,而且可能更快:
sed's / \ | ## \ | // x00 / g'| grep -z'“'
这只是替换行尾模式| ## |使用空字符,然后要求grep查找报价,同时将空字符视为行尾。
答案 1 :(得分:0)
此答案提供了两个解决方案:Gnu Awk解决方案和POSIX版本。
POSIX awk
awk '{r=r ? r "\n" $0 : $0}
/\|##\|$/ { if (r ~ /"/) print r; r=""}' inputfile > outputfile
GNU awk 1
awk 'BEGIN{RS="\\|##\\|\n?";ORS="|##|\n"}/"/' inputfile > outputfile
GNU awk 2
awk 'BEGIN{RS="\\|##\\|\n?"}/"/{printf $0 RT}' inputfile > outputfile
在问题中提供的样本数据上,所有提供的解决方案均提供以下输出:
10358|*|BI-MED-CDMA-MCS-90-118-EXAM|*|Exam for 001-MCS-90-118:
Planning, Conducting and Reporting Post Marketing Surveillance "Studies and Safety Reporting from Non Trial Activities |*|GLOBAL_MEDICAL|*||*|Y|*|N|*||*|CFC6E822849A0A7AE040800AA5644B19|*|finke|*|2012.04.30 04:23:27|##|
注意::如果文件来自Windows计算机,则可能会遇到回车问题。在使用这些工具之前,请先在文件上运行dos2unix
。
这是如何工作的? (POSIX)
我们可以使用awk的POSIX版本
awk '{r=r ? r "\n" $0 : $0}
/\|##\|$/ { if (r ~ /"/) print r; r=""}' inputfile > outputfile
这个想法是通过将每行追加到r
来建立记录r
。如果当前行以"|##|"
结尾,则我们检查记录r
是否包含<双引号> "
。在这种情况下,我们将打印记录r
并将记录r
重置为空字符串。如果它不包含<双引号>,我们将其重置。
这是如何工作的? (GNU)
使用GNU awk,您可以直接使用记录分隔符RS
awk 'BEGIN{RS="\\|##\\|\n?";ORS="|##|\n"}/"/' inputfile > outputfile
这里的想法是文件包含各种记录。 OP明确指出,记录的信息分为|*|
分隔的字段,但更重要的是,记录本身由|##|
分隔。因此,在当前的OP示例中,第一个记录是第1行,而第二个记录分布在第2行和第3行。
在awk中,可以通过变量RS
定义记录分隔符。在RS
的默认状态下,它是\n
,它使每行成为一个单独的记录,可由$0
引用。在POSIX中,记录分隔符只能是用于分隔记录的单个字符,而在Gnu awk中,它可以是正则表达式(请参见下面的附录)。
由于OP的记录分隔符为字符串“ | ## |”在所有跟或不跟一个\n
的后面,我们需要定义RS=\\|##\\|\n?
。为什么这么复杂?
|
符号是正则表达式中的OR运算(替代运算符),因此我们需要对其进行转义。但是,由于对用作正则表达式的字符串文字进行了两次解析,因此我们还需要对其进行两次转义。所以|
→\\|
(请参阅here)
\n?
是因为看起来实际的记录分隔符是字符串“ | ## | \ n”,但是某些记录可能没有换行符,尤其是最后一条记录。 / p>
打印记录时,使用print
语句会在每行之后自动附加输出记录分隔符ORS
。默认情况下,它还是一个\n
。由于记录分隔符RS
不是记录$0
的一部分,因此您需要将值ORS
更新为ORS="|##|\n"
。这次,不是正则表达式,因此您根本不需要逃脱。
语句/"/
是/"/{print $0}
的简写,表示如果当前记录$0
包含<双引号> "
,则打印当前记录$0
,然后是输出记录分隔符ORS
。
注意:由于我们实际上已经在使用Gnu awk,因此实际上我们可以将整个过程简化为:
awk 'BEGIN{RS="\\|##\\|\n?"}/"/{printf $0 RT}' inputfile > outputfile
哪个使用与RT
找到的文本相对应的匹配记录分隔符RS
。通过将print
语句替换为printf
语句,我们不再需要ORS
,只需手动将RT
添加到记录$0
。
RS
:输入记录分隔符。它的默认值是一个包含单个换行符的字符串,这意味着输入记录由一行文本组成。它也可以是空字符串,在这种情况下,记录由空白行分隔。如果是正则表达式,则记录将由输入文本中的正则表达式匹配项分隔。
RS
成为正则表达式的能力是gawk
的扩展。在大多数其他AWK实现中,或者如果gawk
处于兼容模式(请参阅选项),则仅使用RS
值的第一个字符。
ORS
::输出记录分隔符。在每个打印语句的末尾输出。它的默认值是换行符“ \ n”。
RT
:(特定于GNU AWK)与记录分隔符RS
表示的文本匹配的输入文本。每次读取记录时都会设置它。