Bash 在文件中搜索双引号(";),并将整行复制到不同的文件中
我需要通读所有文件并查找(“),然后将整行复制到另一个文件中。这里的挑战是当行中有新字符时识别整行 文件格式如下-值用分隔符Bash 在文件中搜索双引号(";),并将整行复制到不同的文件中,bash,shell,awk,sed,Bash,Shell,Awk,Sed,我需要通读所有文件并查找(“),然后将整行复制到另一个文件中。这里的挑战是当行中有新字符时识别整行 文件格式如下-值用分隔符*.分隔,并以|#|结尾 在所附的(图像)中,以绿色突出显示的应该转到新文件,逻辑将检查“,如果它发现读取行从(在|##| |到下一个|#|)开始) 假设您的意思是|##|之间的部分应视为换行符,下一个问题是您的文件是否包含任何真正的换行符?如果不是这样,grep可能不会非常有效,因为它是逐行工作的。如果有任何真正的新词被认为是文本的一部分,那么毫无疑问,格雷普会不高兴的
*.
分隔,并以|#|
结尾
在所附的(图像)中,以绿色突出显示的应该转到新文件,逻辑将检查“
,如果它发现读取行从(在|##| |到下一个|#|)开始)
假设您的意思是
|##|
之间的部分应视为换行符,下一个问题是您的文件是否包含任何真正的换行符?如果不是这样,grep
可能不会非常有效,因为它是逐行工作的。如果有任何真正的新词被认为是文本的一部分,那么毫无疑问,格雷普会不高兴的
如果您真的想在1中完成,请使用grep:
(3)本周四周四周四周四周四周四周四周四周四周五周五周五周五周五-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |$)'
这是查找以|#|#|开头(或文件开头)后跟一些字符、引号和更多字符,然后以|##|(或文件结尾)结尾的任何序列。使用-z grep将忽略文件中的任何换行。
复杂的“任意字符”([^ |][124;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/code>表达式是因为grep是贪婪的。它基本上寻找的是不属于贪婪的重复序列。也许关闭贪婪是好的,但这将取决于grep版本中regexp引擎的强大程度
但使用sed分解记录并注入“NULL”换行符要容易得多,而且可能更快:
sed's/\\\\\\\\\\\\\\\\\x00/g''grep-z''
这只是将您的行尾模式|##|替换为空字符,然后让grep查找引号,同时将空字符视为行尾。此答案提供了两种解决方案—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)
使用POSIX版本的
awk '{r=r ? r "\n" $0 : $0}
/\|##\|$/ { if (r ~ /"/) print r; r=""}' inputfile > outputfile
这个想法是通过将每一行追加到r
来建立一个记录r
。如果当前行以“|#| |”
结尾,则检查记录r
是否包含”
。如果是这种情况,则打印记录r
,并将记录r
重置为空字符串。如果它不包含
这是如何工作的?(GNU)
使用GNU,您可以直接使用记录分隔符RS
awk 'BEGIN{RS="\\|##\\|\n?";ORS="|##|\n"}/"/' inputfile > outputfile
这里的想法是,文件包含各种记录。OP清楚地说明,记录的信息被拆分为由*.
分隔的字段,但更重要的是,记录本身被.\35;.
分隔。因此,在OP的示例中,第一条记录是第1行,而第二条记录是分散的第2行和第3行
在中,您可以通过变量RS
定义记录分隔符。在其默认状态下,RS
是字符\n
,它使每一行都成为一个单独的记录,可以被$0
引用。在POSIX中,记录分隔符只能是一个单独的字符,用于分隔记录,而在Gnu awk中,这可以是一个正则表达式(见下面的附录)
由于OP的记录分隔符是字符串“|##|”后跟或不后跟字符\n
,因此我们需要定义RS=\\\\\\\\\\\\\\\\n?
。为什么这么复杂
符号是正则表达式中的OR运算(交替运算符),因此我们需要对其进行转义。但是,由于用作正则表达式的字符串文本被解析两次,因此我们也需要对其进行转义两次。因此
→\\\\
(请参阅)
- 之所以使用
\n?
是因为实际的记录分隔符似乎是字符串“|##|\n”,但可能有些记录没有换行符,尤其是最后一条记录
打印记录时,使用print
语句,它会在每行后面自动附加输出记录分隔符ORS
。默认情况下,这也是一个字符\n
。由于记录分隔符RS
不是记录$0
的一部分,因此需要将值ORS
更新为>ORS=“|#| |\n”
。这次不是正则表达式,所以根本不需要转义
语句/“/
是/“/{print$0}
的简写,这意味着如果当前记录$0
包含“
,则打印当前记录$0
,后跟输出记录分隔符OR
注意:由于我们实际上已经使用了Gnu awk,我们实际上可以将整个过程进一步简化为:
awk 'BEGIN{RS="\\|##\\|\n?"}/"/{printf $0 RT}' inputfile > outputfile
它使用匹配的记录分隔符RT
,该分隔符对应于RS
找到的文本。通过将print
语句替换为printf
语句,我们不再需要or
,只需手动将RT
添加到记录$0
RS
:输入记录分隔符。它的默认值是一个包含单个换行符的字符串,这意味着输入记录由一行文本组成。它也可以是
awk 'BEGIN{RS="\\|##\\|\n?"}/"/{printf $0 RT}' inputfile > outputfile