我有一个包含目录列表的文件:
/a/b
/a/b/c
/a/b/d /a/ b
/e
/a/c
/a/c/b
/a/c/d
/a/d/e
/a/d/e /f
/a/e/f/g /a/e/f/g
/h
...
我只想获取 /a/b、/a/c、/a/d/e 和 /a/e/f/g;也就是说,我想排除前面有另一行的子集的行。子目录的深度是任意的,因此我可以向下 2、3、4 等目录查找唯一的子目录。
答案1
假设您的输入已排序,那么检查前缀并在其更改时更新它怎么样?
$ awk 'NR == 1 || ! match($0, "^" pfx) {print; pfx = $0}' file
/a/b
/a/c
注意:这是正则表达式匹配,因此如果条目包含正则表达式特殊字符,则可能不合适 - FWIW 两者都没有gawk
或mawk
似乎/
在此上下文中将 视为特殊字符
答案2
gawk -F/ '
{
# have we seen something that is a prefix of this line?
for (prefix in prefixes)
if ($0 ~ "^" prefix)
# yes we have
next
prefixes[$0] = 1
# are there prefixes that get "cancelled out" by this new one?
# e.g. /a/b/c is already a prefix but current line is /a/b
for (prefix in prefixes)
if (prefix ~ "^" $0 ".+")
delete prefixes[prefix]
}
END {
# GNU awk: traverse the array by index, sorted
PROCINFO["sorted_in"] = "@ind_str_asc"
for (p in prefixes)
print p
}
' list_of_dirs
输出
/a/b
/a/c
/a/d/e
/a/e/f/g
如果您没有 GNU awk,则将输出通过管道传输到| sort
答案3
$ awk -F/ 'NF==3 { print }' filename
我们将字段分隔符设置为/
,然后打印仅包含三个字段的行。假设您的输入文件格式一致,则/a/b
只会打印诸如 之类的行,因为这三个字段依次为空字符串、a
和b
。
答案4
您可以使用编辑器执行此操作,sed
如下所示:
$ sed -e '
$!N
\|^\(.*\)\n\1/|!{P;D;}
s/\n.*//;H;s/.*//;x;D
' input_file
/a/b
/a/c
/a/d/e
/a/e/f/g
在职的:
- 确保图案空间中随时有两条线。
- 如果在模式空间的第二部分的前导位置没有找到第一部分=>它们不属于同一分支。我们打印第一部分,将其删除,然后返回将下一行读入模式空间,并执行相同的检查。
- 在匹配的情况下,我们删除第二部分,因为这是较大的部分(由于排序输入假设),所以我们继续并立即删除该部分。然后返回并将下一行读入模式空间,然后冲洗/重复。
如果输入未排序,您可以按以下方式进行操作:
$ perl -lne '
my $l = $_;
grep !index($l,$_), keys %h or $h{$_}++;
}{print for sort keys %h;
' input
/a/b
/a/c
/a/d/e
/a/e/f/g
在职的:
- index(str, substr) 将返回在 str 中找到 substr 的索引。为了在开头匹配,返回 0,然后将其布尔值反转以使其读取为成功。 grep 将迭代哈希 %h 的所有当前键,其键是我们想要的子字符串。