如何从文件中删除空格并从另一个文件中提取相应的索引? - bash

时间:2015-01-28 12:05:33

标签: python regex bash awk sed

我有三个文件:

FILE1.TXT

XYZ与ABC
DFC什么
FBFBBBFde
warlaugh世界

FILE2.TXT

XYZ 与 ABC
warlaugh 世界

file3.txt

XYZ with abc
DFC whatever
FBFBBBF
world of warlaugh

file2.txt是来自file1.txt的带空格的已处理文件。 file1.txt的行与file3.txt对齐,即foobaristhehelloworld <-> XYZ with abc

由于某些原因,处理从file2.txt丢弃了行,但更重要的是在处理后从file3.txt检索相应的行。

如何在file2.txt中检查哪些行已被删除,然后生成如下所示的file4.txt

file4.txt

XYZ with abc
world of warlaugh

我可以用python做到这一点,但我确信sed / awk或bash技巧有一个简单的方法:

with open('file1.txt', 'r') as file1, open('file2.txt') as file2, open('file3.txt', 'r') as file3:
    file2_nospace = [i.replace(' ', '') for i in file2.readlines()]
    file2_indices = [i for i,j in enumerate(file1.readlines()) if j in file2_nospace]
    file4 = [j for i,j in enumerate(file3.readlines()) if i in file2_indices]

    open('file4.txt', 'w').write('\n'.join(file4)) 

如何使用sed / awk / grep或bash技巧创建file4.txt?

4 个答案:

答案 0 :(得分:1)

首先删除file2.txt中的空格,使其行显示为file1.txt

sed 's/ //g' file2.txt

然后将其用作与file1.txt匹配的模式。使用grep -f命令执行此操作,并使用-n查看与file2.txt中构造的模式匹配的file1.txt的行号:

$ grep -nf <(sed 's/ //g' file2.txt) file1.txt
1:XYZ与ABC
4:warlaugh世界

现在您需要删除:之后的任何字符,以使新模式与file3.txt行匹配:

$ grep -nf <(sed 's/ //g' file2.txt) file1.txt | sed 's/:.*/:/'
1:
4:

file3.txt的每一行添加行号,请使用:

$ nl -s':' file3.txt | sed -r 's/^ +//'
1:XYZ with abc
2:DFC whatever
3:FBFBBBF
4:world of warlaugh

现在您可以将第一个输出用作模式以匹配第二个输出:

$ grep -f <(grep -nf <(sed 's/ //g' file2.txt) file1.txt | sed 's/:.*/:/')  <(nl -s':' file3.txt | sed -r 's/^ +//')
1:XYZ with abc
4:world of warlaugh

要删除起始行号,只需使用cut

$ grep -f <(grep -nf <(sed 's/ //g' file2.txt) file1.txt | sed 's/:.*/:/')  <(nl -s':' file3.txt | sed -r 's/^ +//') | cut -d':' -f2
XYZ with abc
world of warlaugh

最后将结果保存到file4.txt

$ grep -f <(grep -nf <(sed 's/ //g' file2.txt) file1.txt | sed 's/:.*/:/')  <(nl -s':' file3.txt | sed -r 's/^ +//') | cut -d':' -f2 > file4.txt

答案 1 :(得分:1)

您可以通过一次调用awk来同样地执行此操作:

awk 'FILENAME ~ /file2.txt/ { gsub(/ /, ""); a[$0]; next }
     FILENAME ~ /file1.txt/ && $0 in a { b[FNR]; next }
     FILENAME ~ /file3.txt/ && FNR in b { print }' file2.txt file1.txt file3.txt

您还可以使用两个awk来避免使用FILENAME变量:

awk 'FNR==NR { gsub(/ /, ""); a[$0]; next } 
    $0 in a { print FNR }' file2.txt file1.txt | 
awk 'FNR==NR { a[$0]; next } FNR in a { print }' - file3.txt

使用> file4.txt输出到file4.txt之后。

基本上是

  • 取出file2.txt并在剥离空格后将其存储在关联数组中。
  • 将行号存储格式file1.txt与该关联数组进行比较,并按文件行号将其存储在另一个关联数组中。
  • 测试以查看file3.txt中的行号是否在第二个关联数组中,并在匹配时打印。

答案 2 :(得分:1)

循环遍历原始文件,并在file2中查找相应的行。 当行匹配时,从file3打印相应的行。

linenr=0
filternr=1
for line in $(cat file1.txt); do
   (( linenr = linenr + 1 ))
   line2=$(sed -n ${filternr}p file2.txt | cut -d" " -f1)
   if [[ "${line}" = ${line2}* ]]; then
      (( filternr = filternr + 1 ))
      sed -n ${linenr}p file3.txt
   fi
done > file4.txt

当文件很大时(实际上当file2中的行数很大时),你想要改变这个解决方案,避免sed每次都要经过file2和file3。写入/覆盖/维护的解决方案不那么简单......

答案 3 :(得分:1)

在每个文件中查看一次可以使用stdin的diff和重定向来完成 此解决方案仅在您确定没有&#39; |&#39; -character时才有效:

#/bin/bash

function mycheck {
   if [ -z "${filteredline}" ]; then
      exec 0<file2.txt
      read filteredline
   fi
   line2=${filteredline%% *}
   if [[ "${line}" = ${line2}* ]]; then
      echo ${line} | sed 's/.*|\t//'
      read filteredline
      if [ -z "${filteredline}" ]; then
         break;
      fi
   fi
}

IFS="
"
for line in $(diff -y file1.txt file3.txt); do
   mycheck "${line}"
done > file4.txt