繁体   English   中英

当字符串出现在多行中时,如何从文件中删除行?

[英]How can i remove lines from a file when a string appears on multiple lines?

我有一个具有2列的文件,如下所示:

apple pear
banana pizza
spoon fork
pizza plate
sausage egg

如果一个单词出现在多行中,我想删除重复出现的单词的所有行,如您所见,“比萨饼”出现了两次,因此应删除2行,以下是必需的输出:

apple pear
spoon fork
sausage egg

我知道使用:

awk '!seen[$1]++' 

但是,这仅在字符串出现在一列中时才删除行,我需要一条将检查两列的命令。 我怎样才能做到这一点?

您可以使用grepuniq -d多个步骤解决问题。

首先,使用grep -Eo '[^ ]+'类的东西生成所有单词的列表。 然后过滤该列表,以便仅保留重复的单词。 可以使用… | sort | uniq -d进行过滤… | sort | uniq -d … | sort | uniq -d … | sort | uniq -d 最后,从先前使用grep -Fwvf listFile inputFile生成的列表中打印不包含任何单词的所有行。

bash所有这些步骤都可以在一个命令中运行。 在这里,我们将使用变量$in使其易于适应。

in="path/to/your/input/file"
grep -Fwvf <(grep -Eo '[^ ]+' "$in" | sort | uniq -d) "$in"

使用awk,您可以跟踪许多事情。 不仅您看到了一个单词,而且看到了单词的哪一行。 我们跟踪几个数组。

  • record :跟踪我们解析的每一行
  • seen :跟踪各种单词以及它出现的第一个记录号

这给我们:

awk '{ record[NR]=$0 }
     { for(i=1;i<=NF;++i) {
         if ($i in seen) { delete record[NR]; delete record[seen[$i]] }
         else { seen[$i]=NR }
       }
     }
     END { for(i=1;i<=NR;++i) if (i in record) print record[i] }' file 

这是如何运作的?

  • record[NR]=$0 :将记录$0存储在由记录号NR索引的数组record
  • 对于记录的每个字段/单词,请检查该单词之前是否曾出现过。 如果已看到,则从阵列record以及当前记录中删除原始记录。 如果尚未看到,请将单词和当前记录号存储在seeed数组seen
  • 处理完整个文件后,检查我们看到的所有可能的记录号,如果它仍然是数组record的索引,则打印该记录。
$ awk '
    NR==FNR {
        for (i=1; i<=NF;i++) {
            if ( firstNr[$i] ) {
                multi[NR]
                multi[firstNr[$i]]
            }
            else {
                firstNr[$i] = NR
            }
        }
        next
    }
    !(FNR in multi)
' file file
apple pear
spoon fork
sausage egg

或者,如果您喜欢:

$ awk '
    NR==FNR {
        for (i=1; i<=NF;i++) {
            cnt[$i]++
        }
        next
    }
    {
        for (i=1; i<=NF;i++) {
            if ( cnt[$i] > 1 ) {
                next
            }
        }
        print
    }
' file file
apple pear
spoon fork
sausage egg

这适用于您的示例:

#!/usr/bin/env sh
filename='x.txt'
for dupe in $(xargs -n1 -a "${filename}" | sort | uniq -d); do
  sed -i.bak -e "/\\<${dupe}\\>/d" "${filename}"
done

它建立一个单词列表,该单词列表在文件中出现多次:

  • xargs -n1 -a "${filename}"输出所有单词的列表
    包含在文件中(每行一个字)
  • | sort | sort排序列表
  • | uniq -d | uniq -d仅输出连续出现多次的单词

然后使用sed选择并删除所有包含重复单词的行。

这可能适合您(GNU grep,sort,uniq,sed):

sed 's/ /\n/g' file | sort |uniq -d | grep -vFf - file

或玩具GNU sed解决方案:

cat <<\! | sed -Ef - file
H         # copy file into hold space
$!d       # delete each line of the original file
g         # at EOF replace pattern space with entire file
y/ /\n/;  # put each word on a separate line
# make a list of duplicate words, space separated
:a;s/^(.*\n)(\S+)(\n.*\b\2\b)/\2 \1\3/;ta
s/\n.*//  # remove adulterated file leaving list of duplicates
G         # append original file to list
# remove lines with duplicate words
:b;s/^((\S+) .*)\n[^\n]*\2[^\n]*/\1/;tb
s/^\S+ //;tb # reduce duplicate word list
s/..//    # remove newline artefacts
!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM