如果前一行是前缀,则删除行(省略输出)

时间:2018-05-26 23:52:56

标签: bash awk

这与问题" remove duplicate lines with similar prefix"非常相似但事实恰恰相反:

给出排序字符串(在本例中为目录)的输入,如:

a/
a/b/c/
a/d/
bar/foo/
bar/foo2/
c/d/
c/d/e/

我想从输出中删除行,如果前一行是当前行的前缀。在这种情况下,输出将是:

a/
bar/foo/
bar/foo2/
c/d/

在Python等中编码很容易,但在这种情况下我在shell环境中(bash,sort,sed,awk ......)。 (重新排序很好。)

4 个答案:

答案 0 :(得分:4)

使用awk:

awk '{if(k && match($0, k))next; k="^"$0}1' file

k =" ^" $ 0将模式锚定到字符串的开头。

如果有EMPTY行,可能需要在主块之前NF>0

更新:如果变量k中存在正则表达式元字符可能会出现问题,不使用正则表达式的下一行应该更好:

awk '{if(k && index($0, k)==1)next; k=$0}1' file

更新-2:感谢@Ed,我已经调整了第二种方法来覆盖评估为零的非空行(尽管空行将保持原样):< / p>

awk '{if(k!="" && index($0,k)==1)next;k=$0}1' file

答案 1 :(得分:2)

Bash本身(实际上是POSIX shell)通过参数扩展和子串删除提供了所需的一切。您需要做的就是检查您读取的行是否与删除的前缀匹配。如果没有,则您有一个前缀行,否则,您有一个非前缀行。然后输出非前缀行并将前缀设置为当前行是一件简单的事情 - 并重复,例如。

#!/bin/bash

pfx=    ## prefix

## read each line
while read -r line; do 
    ## if no prefix or line matches line with prefix removed
    if [ -z "$pfx" -o "$line" = "${line#$pfx}" ]
    then
        printf "%s\n" "$line"   ## output lile
        pfx="$line"             ## set prefix to line
    fi
done < "$1"

注意:如果输入文件有可能不包含POSIX文件结尾,例如文件最后一行的'\n',那么您应该检查行的内容作为while的条件,例如while read -r line || [ -n "$line" ]; do ...

示例输入文件

$ cat string.txt
a/
a/b/c/
a/d/
bar/foo/
bar/foo2/
c/d/
c/d/e/

示例使用/输出

$ bash nonprefix.sh string.txt
a/
bar/foo/
bar/foo2/
c/d/

答案 2 :(得分:1)

Perl 1-liner。循环输入行-n,然后执行-e以下程序,检查当前行的开头是否与最后一行匹配,打印不匹配。

perl -ne 'print unless m|^$last|; chomp($last=$_);' file_list.txt

答案 3 :(得分:1)

$ awk 'NR==1 || index($0,prev)!=1{prev=$0; print}' file
a/
bar/foo/
bar/foo2/
c/d/