原始问题:我有一个域的子域列表,例如a.domain.fr
,b.domain.fr
等。域本身可以在列表中。
我想从此域列表中找到domain.fr
,该域正在查找最长的公共后缀,该后缀不以点.
开头。
域列表是一个bash字符串,并且域之间用一个空格分隔。
我读了Longest common prefix of two strings in bash,但没有设法将其转换为后缀:
echo $domains | tr ' ' '\n' | sed -e 'N;s/^.*\(.*\)\n.*\1$/\1/'
...打印一堆空行,然后:
echo $domains | tr ' ' '\n' | sed -e 'N;s/^.*\.\(.*\)\n.*\.\1$/\1/'
...打印一堆fr
。
我不是在寻求极高的可移植性,无需额外安装即可在任何Linux发行版上运行的东西对我来说都是可以的。
我正在寻找一种可以找到子域作为通用“域”的解决方案,例如以下列表:
a.d.domain.fr b.d.domain.fr c.d.domain.fr
...公共域应为d.domain.fr
,但是如果您有一个仅适用于顶级域的有效解决方案(例如,上面的列表将返回domain.fr
),我就是也有兴趣。
样本字符串(每行一个样本):
a.domain.fr domain.fr b.a.domain.fr b.domain.fr u.domain.fr
domain.fr
a.domain.fr
a.domain.fr b.domain.fr domain.fr
a.d.domain.fr b.d.domain.fr c.d.domain.fr
答案 0 :(得分:3)
You can use awk to compare each part of the domain one by one and keep track of the number of common parts:
# on the first line
NR == 1 {
# split first domain into "parts" for comparison with rest
n = split($1, parts, /\./)
# initialise result
c = n
}
# on every line
{
for (i = 1; i <= NF; ++i) {
# split current record into "s"
m = split($i, s, /\./)
# increment j as long as the last elements of "parts" match "s"
for (j = 0; j < c && parts[n-j] == s[m-j]; ++j);
# update count if lower
if (j < c) c = j
}
}
# print the result, joining the parts with a "." and ending with a newline
END { for (i = 1; i <= c; ++i) printf "%s%s", parts[n-c+i], (i < c ? "." : ORS) }
Save the script and run it like awk -f script.awk file
.
答案 1 :(得分:2)
将最长的结尾字符串与sed匹配有点困难,因为第一个.*
将吞噬输入中的所有字符。但是我们可以只是个虚拟对象,而只需rev
就可以输入字符串。我还添加了\.
来与sed内部匹配,因此domain.fr
和not_in_domain.fr
不会导致domain.fr
,而会导致fr
。
printf "%s\n" a.domain.fr b.domain.fr | rev | sed -e 'N;s/^\(.*\)\..*\n\1\..*$/\1/' | rev
将输出:
domain.fr
由于此sed
一次只能处理两个字符串,因此对于更多的表达式,我们必须将其“折叠”:
printf "%s\n" a.a.domain.fr b.a.domain.fr b.not_in_a.domain.fr |
rev |
{
# the function
f() {
printf "%s.\n" "$@" |
sed -e 'N;s/^\(.*\)\..*\n\1\..*$/\1/';
};
# load initial
IFS= read -r res;
# for each line
while IFS= read -r line; do
# right fold it
res=$(f "$res" "$line");
done;
printf "%s\n" "$res";
} | rev
@edit通过将其包含在sed中来固定匹配的前导点
答案 2 :(得分:1)
这一行awk可以满足您的期望:
awk '{d=$1; for(i=2;i<=NF;++i) while(d && ! match($i,d"$")) sub(/[^.]*./,"",d); print d}'
上述解决方案仅会打印匹配的域部分。
如果要使其更坚固,则必须对其进行一些更正,因为:
* match
将匹配一个正则表达式,而.
将匹配任何字符
*您必须确保ere
以.
开头
在第一个示例中没有处理这些事情。
更正在这里:
awk '{d=$1; gsub(/[.]/,"\\.",d); for(i=2;i<=NF;++i) while(d && ! match($i,"(^|[.])"d"$")) { sub(/[^.]*([.]|$)/,"",d)}; gsub(/[\\][.]/,".",d);print d}'
答案 3 :(得分:1)
这是一个纯Bash程序,包含可能的解决方案:
#! /bin/bash -p
# A space-separated list of domains
domainlist=$1
longest=
longest_rx='\.([^ ]*) .*\.\1$'
for domain in $domainlist ; do
if [[ -z $longest ]] ; then
longest=$domain
elif [[ ".$longest .$domain" =~ $longest_rx ]] ; then
longest=${BASH_REMATCH[1]}
else
longest=
break
fi
done
printf "longest='%s'\n" "$longest"
./progname 'a.d.domain.fr b.d.domain.fr c.d.domain.fr'
(输出longest='d.domain.fr'
)。\1
)的Bash正则表达式。对于Linux(在具有10年以上历史的系统上使用Bash 3进行测试),这应该是可以的,但对于某些其他系统(包括某些Unix系统)则不是。答案 4 :(得分:0)
我喜欢编写awk程序来解决此问题。 该程序使用提供的字符串列表查找最长的后缀字符串和最短的后缀字符串。
最长的公共后缀可以更长,而最短的公共后缀是1个字符。
匹配算法找到与提供的字符串右侧匹配的地方。
{
# read the fields into a unique array
for(i = 1; i <= NF; i++){
if ($i in uniquenessArr == 0) { #accept a field into arr only if not in the uniquenessArr
uniquenessArr[$i] = 1;
arr[++arrLen] = $i;
}
}
# arrLen is count of fields to compute
minLen = 9999999; # initial length of minimal matched string
for(currStr in arr){ # for each string in arr
len = length(arr[currStr]);
# print currStr ") " arr[currStr] " (" len ")";
for(targetStr in arr) { # match each string against longer strings in arr
if ( (len < length(arr[targetStr])) && match(arr[targetStr], arr[currStr]"$") ) {
# currStr is matched into a longer string
if (maxLen <= RLENGTH ) {
maxLen = RLENGTH;
maxMatch = arr[currStr];
}
if (minLen >= RLENGTH ) {
minLen = RLENGTH;
minMatch = arr[currStr];
}
}
}
}
printf("maxMatch = %s \t minMatch = %s\n", maxMatch, minMatch);
}
a.domain.fr domain.fr b.a.domain.fr b.domain.fr u.domain.fr
domain.fr
a.domain.fr
a.domain.fr b.domain.fr domain.fr
a.d.domain.fr b.d.domain.fr c.d.domain.fr d.domain.fr c.b.d.domain.fr b.c.d.domain.fr
awk -f script-1.awk input
第一个for循环将所有字段读入一组(无重复) 逻辑是针对较长的字符串扫描每个字符串。 如果找到匹配项,则标记最长和最短的匹配字符串。