如何从URL中提取域名?

63
如何在Bash中从URL中提取域名? 例如: http://example.com/ 转成 example.com 必须适用于任何顶级域名,而不仅仅是 .com。

复制:https://dev59.com/fnRA5IYBdhLWcg3wyRF6 - Dennis Williamson
那是Perl,不是Bash。 - user181548
基本上这里所有的答案都是错误的,除了令人困惑的 Ruby 答案。在决定哪个是根域之前,您需要了解顶级域的子域策略。查找 Public Suffic 数据库。简而言之,您需要处理像 www.surrey.bbc.co.ukwww.nic.ad.jpwww.city.nagoya.jp 等情况。 - tripleee
@tripleee:今天发布了一个纯Bash答案,其中包含一个章节回应了您的评论! - F. Hauri - Give Up GitHub
16个回答

99
您可以使用简单的 AWK 方法提取域名,如下所示:
echo http://example.com/index.php | awk -F[/:] '{print $4}'

输出:example.com

:-)


11
echo http://example.com:3030/index.php | awk -F/ '{print $3}' 翻译成中文后是:example.com:3030 - Ben Burns
你可以再次按 : 分割它,但它不够灵活,无法接受带或不带端口的情况。 - chovy
| awk -F/ '{print $3}' | awk -F: '{print $1}' | awk -F/ '{print $3}' | awk -F: '{print $1}' - Andrew Mackenzie
3
我是通过使用以下命令得到的 - echo http://www.example.com/somedir/someotherdir/index.html | cut -d'/' -f1,2,3,得到的结果是 http://www.example.com - 3AK
7
处理带有端口和不带端口的URL:awk -F[/:] '{print $4}' - Michael
显示剩余6条评论

37

3
这可以在带端口或不带端口、深度路径的情况下工作,并且仍然使用bash。尽管它在Mac上不起作用。 - chovy
7年过去了,这仍然是我首选的答案。 - mwoodman
2
我使用你的建议,并添加一些额外的内容来剥离URL中可能存在的任何子域名 ->> echo http://www.mail.example.com:3030/index.php | sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/" | awk -F. '{print $(NF-1) "." $NF}',因此基本上我通过点将你的输出划分,并取最后两列并用点连接它们。 - sakumatto
这是最好的答案!我将其用于允许完整URL的ping命令:https://unix.stackexchange.com/a/428990/20661,仅剥离“www.”子域名。 - rubo77
1
想要获取端口的人可以使用以下命令:sed -e "s/[^/]*\/\/\([^@]*@\)\?\([^:/]*\)\(:\([0-9]\{1,5\}\)\)\?.*/\4/" - wheeler
1
@sakumatto 运行良好,但是如果支持例如 "https://example.com.uk" 会怎样呢? - sanNeck

30
basename "http://example.com"

当然,这种URI http://www.example.com/index.html 是行不通的,但是你可以采取以下方法:

basename $(dirname "http://www.example.com/index.html")

或者对于更复杂的URI:

echo "http://www.example.com/somedir/someotherdir/index.html" | cut -d'/' -f3

-d 意味着 "分隔符",而 -f 意味着 "字段";在上面的示例中,由斜杠 '/' 分隔的第三个字段是 www.example.com。


5
我喜欢使用 cut -d'/' -f3,因为它很简单。 - Jamie Kitson
1
如果添加端口,则会失败:echo "http://www.example.com:8080/somedir/someotherdir/index.html" | cut -d'/' -f3 - chovy
通过运行以下命令:echo http://www.example.com/somedir/someotherdir/index.html | cut -d'/' -f1,2,3,获取了此链接 - http://www.example.com - 3AK
basename $(dirname does not work, if the url ends with the domain like: basename $(dirname "http://www.example.com/") will show just: http: - rubo77

18
echo $URL | cut -d'/' -f3 | cut -d':' -f1

适用于以下URL:

http://host.example.com
http://host.example.com/hi/there
http://host.example.com:2345/hi/there
http://host.example.com:2345

1
我发现这更有用,因为当URL不包含“http://”时,它会将URL返回为原样,即abc.com将保留为abc.com - Udayraj Deshmukh
这实际上是所有答案中最直观、简洁和有效的方法! - Robert
1
这会提取host.example.com而不是所要求的域名(example.com)。 - Lucas

11
sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_'

例如

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'http://example.com'
example.com

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'https://example.com'
example.com

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'http://example.com:1234/some/path'
example.com

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'http://user:pass@example.com:1234/some/path'
example.com

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'http://user:pass@example.com:1234/some/path#fragment'
example.com

$ sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< 'http://user:pass@example.com:1234/some/path#fragment?params=true'
example.com

瞧!HOST=$(sed -E -e 's_.*://([^/@]*@)?([^/:]+).*_\2_' <<< "$MYURL") 在 Bash 中是可以的。 - 4Z4T4R
我想从域名中删除www。在这种情况下,我应该如何更改命令? - Ceylan B.
谢谢这个,非常方便,为了从URL获取路径,我稍微扩展了一下 sed -E -e 's_.*://([^/@]*@)?([^/:]+)(.*)_\2_' <<< 'http://example.com'这将允许您从URL中抓取路径 sed -E -e 's_.://([^/@]@)?([^/:]+)(.*)\3' <<< 'http://example.com/path/to/something' - Max Barrass

7
#!/usr/bin/perl -w
use strict;

my $url = $ARGV[0];

if($url =~ /([^:]*:\/\/)?([^\/]+\.[^\/]+)/g) {
  print $2;
}

使用方法:

./test.pl 'https://example.com'
example.com

./test.pl 'https://www.example.com/'
www.example.com

./test.pl 'example.org/'
example.org

 ./test.pl 'example.org'
example.org

./test.pl 'example'  -> no output

如果你只需要域名而不是完整的主机名+域名,可以使用以下方法:

#!/usr/bin/perl -w
use strict;

my $url = $ARGV[0];
if($url =~ /([^:]*:\/\/)?([^\/]*\.)*([^\/\.]+\.[^\/]+)/g) {
  print $3;
}

如果有API的话,我肯定会选择使用它。看起来完整的解决方案实际上需要知道所有有效的国家代码,并检查最后一个点后面的区域是否是国家代码... - Dark Castle

6
3个答案:短URL解析(+)和完整的顶级域名提取器
关于问题的备注:
问题代表,但目标是在/字符上“分割”字符串!使用正则表达式来完成这种任务是杀鸡用牛刀!
首先是Posix shell
不必再使用forks到其他二进制文件,比如awk、perl、cut等等,我们可以使用参数扩展来实现更快的操作。
URL="http://example.com/some/path/to/page.html"
prot="${URL%%:*}"
link="${URL#$prot://}"
domain="${link%%/*}"
link="${link#$domain}"
printf '%-8s: %s\n' Protocol "${prot%:}" Domain "$domain" Link "$link"

Protocol: http
Domain  : example.com
Link    : /some/path/to/page.html

注意:这项工作即使使用 file URL也可以完成:

URL=file:///tmp/so/test.xml 
prot="${URL%%:*}"
link="${URL#$prot://}"
domain="${link%%/*}"
link="${link#$domain}"
printf '%-8s: %s\n' Protocol "${prot%:}" Domain "$domain" Link "$link"

Protocol: file
Domain  : 
Link    : /tmp/so/test.xml

使用bash读取URL部分
由于这个问题标记为,并且没有回答涉及到read的简短、快速和可靠的解决方案:
URL="http://example.com/some/path/to/page.html"

IFS=/ read -r prot _ domain link <<<"$URL"

那就是全部了。由于read是一个内置函数,这是最快的方法!(**请参见comment
从那里你可以……
printf '%-8s: %s\n' Protocol "${prot%:}" Domain "$domain" Link "/$link"

Protocol: http
Domain  : example.com
Link    : /some/path/to/page.html

你甚至可以检查端口。
URL="http://example.com:8000/some/path/to/page.html"
IFS=/ read -r prot _ domain link <<<"$URL"
IFS=: read -r domain port <<<"$domain"

printf '%-8s: %s\n' Protocol "${prot%:}" Domain "$domain" Port "$port" Link "/$link"

Protocol: http
Domain  : example.com
Port    : 8000
Link    : /some/path/to/page.html

使用默认端口进行完整解析:

URL="https://dev59.com/h3E95IYBdhLWcg3wApLl"
declare -A DEFPORTS='([http]=80 [https]=443 [ipp]=631 [ftp]=21)'
IFS=/ read -r prot _ domain link <<<"$URL"
IFS=: read -r domain port <<<"$domain"

printf '%-8s: %s\n' Protocol "${prot%:}" Domain "$domain" \
    Port  "${port:-${DEFPORTS[${prot%:}]}}" Link "/$link"

Protocol: https
Domain  : stackoverflow.com
Port    : 443
Link    : /questions/2497215/how-to-extract-domain-name-from-url

完整顶级域名提取器(纯bash实现):

关于公共后缀@tripleee'的评论

在函数初始化时,有一个对wget的分支只执行一次:

declare -A TLD='()'
initTld () { 
    local tld
    while read -r tld; do
        [[ -n ${tld//*[ \/;*]*} ]] && TLD["${tld#\!}"]=''
    done < <(
      wget -qO - https://publicsuffix.org/list/public_suffix_list.dat
    )
}
tldExtract () { 
    if [[ $1 == -v ]] ;then local _tld_out_var=$2;shift 2;fi
    local dom tld=$1 _tld_out_var
    while [[ ! -v TLD[${tld}] ]] && [[ -n $tld ]]; do
        IFS=. read -r dom tld <<< "$tld"
    done
    if [[ -v _tld_out_var ]] ;then
        printf -v $_tld_out_var '%s %s' "$dom" "$tld"
    else
        echo "$dom $tld"
    fi
}
initTld ; unset -f initTld

那么

tldExtract www.stackoverflow.com
stackoverflow com

tldExtract sub.www.test.co.uk
test co.uk

tldExtract -v myVar sub.www.test.co.uk
echo ${myVar% *}
test
echo ${myVar#* }
co.uk

tldExtract -v myVar www2.sub.city.nagoya.jp
echo $myVar 
sub city.nagoya.jp

更快的函数:parseUrl() { local IFS=/ arry;arry=($4);printf -v $1 ${arry%:};printf -v $2 ${arry[2]};printf -v $3 "/${arry[*]:3}";} 可用作 read 的替代品:parseUrl prot domain link "$URL" 以填充 $prot $domain$link 变量。 - F. Hauri - Give Up GitHub

6

不必使用正则表达式,您可以使用Python的urlparse:

 URL=http://www.example.com

 python -c "from urlparse import urlparse
 url = urlparse('$URL')
 print url.netloc"

您可以按照以下方式使用它,或将其放入一个小脚本中。然而,这仍然需要一个有效的方案标识符,根据您的评论,您的输入不一定提供一个。您可以指定一个默认方案,但urlparse期望netloc以'//'开头:

url = urlparse('//www.example.com/index.html','http')

因此,您需要手动添加这些内容,即:

 python -c "from urlparse import urlparse
 if '$URL'.find('://') == -1 then:
   url = urlparse('//$URL','http')
 else:
   url = urlparse('$URL')
 print url.netloc"

3

关于如何获取这些URL的信息非常少,请在下次提供更多信息。URL中是否有参数等等...

与此同时,对于您的示例URL,只需进行简单的字符串操作即可。

$ s="http://example.com/index.php"
$ echo ${s/%/*}  #get rid of last "/" onwards
http://example.com
$ s=${s/%\//}  
$ echo ${s/#http:\/\//} # get rid of http://
example.com

另外一种方法是使用sed(GNU)

$ echo $s | sed 's/http:\/\///;s|\/.*||'
example.com

使用awk

$ echo $s| awk '{gsub("http://|/.*","")}1'
example.com

你的方法不起作用!在cygwin上,执行以下命令:echo http://example.com/index.php | sed -r 's/http://|///g'输出结果为:example.comindex.php而不是example.com请提供一个可行的方法。 - Ben Smith
3
我的方法不起作用,因为你提供的示例网址不同!!而且你没有提供更多有关你想解析哪种类型网址的信息!!下次你应该清楚地写出你的问题,提供输入示例并描述你想要的输出。 - ghostdog74
第二行似乎不正确。我将前两行复制到我的Ubuntu shell中,得到的是_http://example.com/index.php_。 - jpeltoniemi

3
以下代码将输出“example.com”:
URI="http://user@example.com/foo/bar/baz/?lala=foo" 
ruby -ruri -e "p URI.parse('$URI').host"

如果您想了解如何使用Ruby的URI类,可以参考文档


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接