我有一个类似的URL:
sftp://user@host.net/some/random/path
我想从这个字符串中提取用户、主机和路径。任何部分都可以是随机长度。
我有一个类似的URL:
sftp://user@host.net/some/random/path
我想从这个字符串中提取用户、主机和路径。任何部分都可以是随机长度。
[编辑于2019年] 这个答案并不是一个万能的适用于所有情况的解决方案,它旨在提供一个简单的替代Python版本的方法,并且最终具有比原始方法更多的功能。
它以纯Bash方式回答了基本问题,然后我进行了多次修改,以包括评论者需求的一些要求。但我认为,现在添加更多复杂性将使其难以维护。我知道不是所有事情都很明显(例如,检查有效端口需要比较“hostport”和“host”),但我宁愿不要增加更多复杂性。
[原始回答]
假设你的URL作为脚本的第一个参数传递:
#!/bin/bash
# extract the protocol
proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
# remove the protocol
url="$(echo ${1/$proto/})"
# extract the user (if any)
user="$(echo $url | grep @ | cut -d@ -f1)"
# extract the host and port
hostport="$(echo ${url/$user@/} | cut -d/ -f1)"
# by request host without port
host="$(echo $hostport | sed -e 's,:.*,,g')"
# by request - try to extract the port
port="$(echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')"
# extract the path (if any)
path="$(echo $url | grep / | cut -d/ -f2-)"
echo "url: $url"
echo " proto: $proto"
echo " user: $user"
echo " host: $host"
echo " port: $port"
echo " path: $path"
我必须承认,这不是最干净的解决方案,但它不依赖于像Perl或Python这样的其他脚本语言。 (使用其中之一提供解决方案将产生更干净的结果;)
使用您的示例,结果如下:
url: user@host.net/some/random/path
proto: sftp://
user: user
host: host.net
port:
path: some/random/path
这也适用于没有协议/用户名或路径的URL。在这种情况下,相应的变量将包含空字符串。
[编辑]
如果您的bash版本无法处理替换(${1/$proto/}),请尝试此操作:#!/bin/bash
# extract the protocol
proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
# remove the protocol -- updated
url=$(echo $1 | sed -e s,$proto,,g)
# extract the user (if any)
user="$(echo $url | grep @ | cut -d@ -f1)"
# extract the host and port -- updated
hostport=$(echo $url | sed -e s,$user@,,g | cut -d/ -f1)
# by request host without port
host="$(echo $hostport | sed -e 's,:.*,,g')"
# by request - try to extract the port
port="$(echo $hostport | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')"
# extract the path (if any)
path="$(echo $url | grep / | cut -d/ -f2-)"
上面的代码已经过修改(添加了密码和端口分析),并且可以在/bin/sh工作:
# extract the protocol
proto="`echo $DATABASE_URL | grep '://' | sed -e's,^\(.*://\).*,\1,g'`"
# remove the protocol
url=`echo $DATABASE_URL | sed -e s,$proto,,g`
# extract the user and password (if any)
userpass="`echo $url | grep @ | cut -d@ -f1`"
pass=`echo $userpass | grep : | cut -d: -f2`
if [ -n "$pass" ]; then
user=`echo $userpass | grep : | cut -d: -f1`
else
user=$userpass
fi
# extract the host -- updated
hostport=`echo $url | sed -e s,$userpass@,,g | cut -d/ -f1`
port=`echo $hostport | grep : | cut -d: -f2`
if [ -n "$port" ]; then
host=`echo $hostport | grep : | cut -d: -f1`
else
host=$hostport
fi
# extract the path (if any)
path="`echo $url | grep / | cut -d/ -f2-`"
发帖是因为我需要这个功能,所以我写了它(基于@Shirkin的答案,显然),并且我想其他人可能也会喜欢。
原则上,这个解决方案与Adam Ryczkowski在本主题中的解决方案基本相同,但它具有改进的正则表达式,基于RFC3986(带有一些更改)并修复了一些错误(例如,userinfo可以包含“_”字符)。 它还可以理解相对URI(例如提取查询或片段)。
# !/bin/bash
# Following regex is based on https://www.rfc-editor.org/rfc/rfc3986#appendix-B with
# additional sub-expressions to split authority into userinfo, host and port
#
readonly URI_REGEX='^(([^:/?#]+):)?(//((([^:/?#]+)@)?([^:/?#]+)(:([0-9]+))?))?(/([^?#]*))(\?([^#]*))?(#(.*))?'
# ↑↑ ↑ ↑↑↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑
# |2 scheme | ||6 userinfo 7 host | 9 port | 11 rpath | 13 query | 15 fragment
# 1 scheme: | |5 userinfo@ 8 :… 10 path 12 ?… 14 #…
# | 4 authority
# 3 //…
parse_scheme () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[2]}"
}
parse_authority () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[4]}"
}
parse_user () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[6]}"
}
parse_host () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[7]}"
}
parse_port () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[9]}"
}
parse_path () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[10]}"
}
parse_rpath () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[11]}"
}
parse_query () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[13]}"
}
parse_fragment () {
[[ "$@" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[15]}"
}
"$*"
而不是"$@"
。 - glenn jackman#!/usr/bin/env python
import os
from urlparse import urlparse
uri = os.environ['NAUTILUS_SCRIPT_CURRENT_URI']
result = urlparse(uri)
user, host = result.netloc.split('@')
path = result.path
print('user=', user)
print('host=', host)
print('path=', path)
更多阅读:
os.environ
(操作系统相关)urlparse.urlparse()
(URL解析相关)[...]ssh result.netloc
来避免将 user
从 host
中分离出来再重新拼接它们...(并且省去 print
调用)。 - johnsyweb你可以使用bash字符串操作。它很容易学习。如果你在正则表达式上遇到困难,请尝试使用它。由于它来自NAUTILUS_SCRIPT_CURRENT_URI,我猜那个URI可能有端口。因此,我也将这个选项保留了下来。
#!/bin/bash
#You can also use environment variable $NAUTILUS_SCRIPT_CURRENT_URI
X="sftp://user@host.net/some/random/path"
tmp=${X#*//};usr=${tmp%@*}
tmp=${X#*@};host=${tmp%%/*};[[ ${X#*://} == *":"* ]] && host=${host%:*}
tmp=${X#*//};path=${tmp#*/}
proto=${X%:*}
[[ ${X#*://} == *":"* ]] && tmp=${X##*:} && port=${tmp%%/*}
echo "Potocol:"$proto" User:"$usr" Host:"$host" Port:"$port" Path:"$path
proto
表达式需要进行最长匹配而不是最短匹配,因此${X%%:*}
(双百分号)应该使用。否则,对于像ssh://user@some.domain:1234/some/path
这样的有效(但令人困惑)输入,第二个冒号将会匹配而不是第一个,协议将被报告为ssh://user@some.domain
。 - Ti Strga我没有足够的声望来评论,但我对@patryk-obara的答案进行了小修改。
RFC3986 § 6.2.3. 基于协议的规范化 处理
http://example.com
http://example.com/
我发现他的正则表达式不能匹配像http://example.com这样的URL,尽管它们是等效的。但是带斜杠的http://example.com/可以匹配。
我插入了11个字符,将/
改为(/|$)
。这个正则表达式可以匹配/
或字符串结尾。现在http://example.com可以匹配了。
readonly URI_REGEX='^(([^:/?#]+):)?(//((([^:/?#]+)@)?([^:/?#]+)(:([0-9]+))?))?((/|$)([^?#]*))(\?([^#]*))?(#(.*))?$'
# ↑↑ ↑ ↑↑↑ ↑ ↑ ↑ ↑↑ ↑ ↑ ↑ ↑ ↑
# || | ||| | | | || | | | | |
# |2 scheme | ||6 userinfo 7 host | 9 port || 12 rpath | 14 query | 16 fragment
# 1 scheme: | |5 userinfo@ 8 :... || 13 ?... 15 #...
# | 4 authority |11 / or end-of-string
# 3 //... 10 path
我也需要做同样的事情,所以我很好奇能否只用一行代码实现,以下是我得到的结果:
#!/bin/bash
parse_url() {
eval $(echo "$1" | sed -e "s#^\(\(.*\)://\)\?\(\([^:@]*\)\(:\(.*\)\)\?@\)\?\([^/?]*\)\(/\(.*\)\)\?#${PREFIX:-URL_}SCHEME='\2' ${PREFIX:-URL_}USER='\4' ${PREFIX:-URL_}PASSWORD='\6' ${PREFIX:-URL_}HOST='\7' ${PREFIX:-URL_}PATH='\9'#")
}
URL=${1:-"http://user:pass@example.com/path/somewhere"}
PREFIX="URL_" parse_url "$URL"
echo "$URL_SCHEME://$URL_USER:$URL_PASSWORD@$URL_HOST/$URL_PATH"
工作原理:
注意:在对任意输入使用此代码时,请小心,因为该代码容易受到脚本注入攻击。
如果你真的想用 shell 实现,你可以使用 awk 来做出以下简单的操作。这需要知道你实际上将传递多少个字段(例如,有时没有密码,而其他时候则有)。
#!/bin/bash
FIELDS=($(echo "sftp://user@host.net/some/random/path" \
| awk '{split($0, arr, /[\/\@:]*/); for (x in arr) { print arr[x] }}'))
proto=${FIELDS[1]}
user=${FIELDS[2]}
host=${FIELDS[3]}
path=$(echo ${FIELDS[@]:3} | sed 's/ /\//g')
如果您没有awk,但是有grep,并且可以要求每个字段至少有两个字符并且格式相对可预测,则可以执行以下操作:
#!/bin/bash
FIELDS=($(echo "sftp://user@host.net/some/random/path" \
| grep -o "[a-z0-9.-][a-z0-9.-]*" | tr '\n' ' '))
proto=${FIELDS[1]}
user=${FIELDS[2]}
host=${FIELDS[3]}
path=$(echo ${FIELDS[@]:3} | sed 's/ /\//g')
以下是我的看法,它基于一些已有回答的基础上,但也可以处理GitHub SSH克隆URL:
#!/bin/bash
PROJECT_URL="git@github.com:heremaps/here-aaa-java-sdk.git"
# Extract the protocol (includes trailing "://").
PARSED_PROTO="$(echo $PROJECT_URL | sed -nr 's,^(.*://).*,\1,p')"
# Remove the protocol from the URL.
PARSED_URL="$(echo ${PROJECT_URL/$PARSED_PROTO/})"
# Extract the user (includes trailing "@").
PARSED_USER="$(echo $PARSED_URL | sed -nr 's,^(.*@).*,\1,p')"
# Remove the user from the URL.
PARSED_URL="$(echo ${PARSED_URL/$PARSED_USER/})"
# Extract the port (includes leading ":").
PARSED_PORT="$(echo $PARSED_URL | sed -nr 's,.*(:[0-9]+).*,\1,p')"
# Remove the port from the URL.
PARSED_URL="$(echo ${PARSED_URL/$PARSED_PORT/})"
# Extract the path (includes leading "/" or ":").
PARSED_PATH="$(echo $PARSED_URL | sed -nr 's,[^/:]*([/:].*),\1,p')"
# Remove the path from the URL.
PARSED_HOST="$(echo ${PARSED_URL/$PARSED_PATH/})"
echo "proto: $PARSED_PROTO"
echo "user: $PARSED_USER"
echo "host: $PARSED_HOST"
echo "port: $PARSED_PORT"
echo "path: $PARSED_PATH"
这提供了
proto:
user: git@
host: github.com
port:
path: :heremaps/here-aaa-java-sdk.git
对于PROJECT_URL="ssh://sschuberth@git.eclipse.org:29418/jgit/jgit"
,您将获得以下结果
proto: ssh://
user: sschuberth@
host: git.eclipse.org
port: :29418
path: /jgit/jgit
pattern='^(([[:alnum:]]+)://)?(([[:alnum:]]+)@)?([^:^@]+)(:([[:digit:]]+))?$'
if [[ "http://us@cos.com:3142" =~ $pattern ]]; then
proto=${BASH_REMATCH[2]}
user=${BASH_REMATCH[4]}
host=${BASH_REMATCH[5]}
port=${BASH_REMATCH[7]}
fi
它应该比之前的所有示例更快,更节省资源,因为不会生成任何外部进程。