web-dev-qa-db-ja.com

シェルスクリプトでURLを解析する

私は次のようなURLを持っています:

sftp://[email protected]/some/random/path

この文字列からユーザー、ホスト、パスを抽出します。どの部分もランダムな長さにすることができます。

19
umpirsky

Python(この仕事に最適なツール、IMHO)を使用):

_#!/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)
_

参考文献:

10
Johnsyweb

[EDIT 2019]この回答は、すべてを網羅することを意図したものではなく、pythonベースのバージョンであり、元のバージョンよりも多くの機能を持つことになりました。


基本的な質問にはbashのみで回答し、その後、コメント作成者の要求に満ちた手を含めるために、自分で何度も修正しました。この時点では、さらに複雑にするとメンテナンスが難しくなると思います。すべてが単純であるとは限りませんが(たとえば、有効なポートを確認するには、hostportHostを比較する必要があります)、複雑さを増やそうとは思いません。


[元の回答]

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: [email protected]/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-)"
37
Shirkrin

上記は、洗練され(追加されたパスワードとポート解析)、/ 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-`"

投稿b/c必要だったので、私はそれを(@Shirkinの回答に基づいて)明らかにし、他の誰かがそれを高く評価していると考えました。

21
pjz

このソリューションは原則として、このスレッドでは Adam Ryczkowski's と同じように機能しますが、 RFC3986 に基づいて正規表現が改善され、いくつかのエラーが修正されます(例:userinfo) 「_」文字を含めることができます)。これは、相対URIを理解することもできます(クエリやフラグメントを抽出するためなど)。

# !/bin/bash

# Following regex is based on https://tools.ietf.org/html/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]}"
}
6
Patryk Obara

これは、大まかに既存の回答に基づいた私の見解ですが、GitHub SSHクローンURLにも対応できます。

#!/bin/bash

PROJECT_URL="[email protected]: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://[email protected]:29418/jgit/jgit"取得します

proto: ssh://
user: sschuberth@
Host: git.Eclipse.org
port: :29418
path: /jgit/jgit
4
sschuberth

同じことをする必要があるだけなので、それを単一の行で実行できるかどうか知りたくて、これが私が持っているものです:

#!/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:[email protected]/path/somewhere"}
PREFIX="URL_" parse_url "$URL"
echo "$URL_SCHEME://$URL_USER:$URL_PASSWORD@$URL_Host/$URL_PATH"

使い方:

  1. それらのすべてがオプションである場合(ホスト名を除く)、URLのすべての部分をキャプチャするクレイジーなsed正規表現があります
  2. これらのキャプチャグループを使用すると、sed出力の環境変数名と関連する部分(URL_SCHEMEやURL_USERなど)の値が出力されます
  3. evalはその出力を実行し、それらの変数をエクスポートしてスクリプトで使用可能にします
  4. オプションでPREFIXを渡して、出力環境変数名を制御できます

PS:注意このコードはスクリプトインジェクションに対して脆弱であるため、これを任意の入力に使用する場合。

2
Stam

シェルで本当にそれをしたい場合は、awkを使用して次のような簡単なことを行うことができます。これには、実際に渡されるフィールドの数を知っている必要があります(たとえば、パスワードがない場合もあれば、ない場合もあります)。

#!/bin/bash

FIELDS=($(echo "sftp://[email protected]/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があり、各フィールドに少なくとも2文字が必要であり、形式が適度に予測可能であることを要求できる場合は、次のようにすることができます。

#!/bin/bash

FIELDS=($(echo "sftp://[email protected]/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')
2
relistan

私は上記の方法が好きではなく、自分で書いた。これはftpリンク用です。必要に応じて、ftphttpに置き換えてください。最初の行はリンクの小さな検証です。リンクはftp://user:[email protected]/path/to/something

if ! echo "$url" | grep -q '^[[:blank:]]*ftp://[[:alnum:]]\+:[[:alnum:]]\+@[[:alnum:]\.]\+/.*[[:blank:]]*$'; then return 1; fi

login=$(  echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\1|' )
pass=$(   echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\2|' )
Host=$(   echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\3|' )
dir=$(    echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\4|' )

私の実際の目標は、URLによるFTPアクセスをチェックすることでした。以下が完全な結果です。

#!/bin/bash

test_ftp_url()  # lftp may hang on some ftp problems, like no connection
    {
    local url="$1"

    if ! echo "$url" | grep -q '^[[:blank:]]*ftp://[[:alnum:]]\+:[[:alnum:]]\+@[[:alnum:]\.]\+/.*[[:blank:]]*$'; then return 1; fi

    local login=$(  echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\1|' )
    local pass=$(   echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\2|' )
    local Host=$(   echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\3|' )
    local dir=$(    echo "$url" | sed 's|[[:blank:]]*ftp://\([^:]\+\):\([^@]\+\)@\([^/]\+\)\(/.*\)[[:blank:]]*|\4|' )

    exec 3>&2 2>/dev/null
    exec 6<>"/dev/tcp/$Host/21" || { exec 2>&3 3>&-; echo 'Bash network support is disabled. Skipping ftp check.'; return 0; }

    read <&6
    if ! echo "${REPLY//$'\r'}" | grep -q '^220'; then exec 2>&3  3>&- 6>&-; return 3; fi   # 220 vsFTPd 3.0.2+ (ext.1) ready...

    echo -e "USER $login\r" >&6; read <&6
    if ! echo "${REPLY//$'\r'}" | grep -q '^331'; then exec 2>&3  3>&- 6>&-; return 4; fi   # 331 Please specify the password.

    echo -e "PASS $pass\r" >&6; read <&6
    if ! echo "${REPLY//$'\r'}" | grep -q '^230'; then exec 2>&3  3>&- 6>&-; return 5; fi   # 230 Login successful.

    echo -e "CWD $dir\r" >&6; read <&6
    if ! echo "${REPLY//$'\r'}" | grep -q '^250'; then exec 2>&3  3>&- 6>&-; return 6; fi   # 250 Directory successfully changed.

    echo -e "QUIT\r" >&6

    exec 2>&3  3>&- 6>&-
    return 0
    }

test_ftp_url 'ftp://fz223free:[email protected]/out/nsi/nsiProtocol/daily'
echo "$?"
1
user3132194

私はさらに解析を行い、@ Shirkrinによって与えられたソリューションを拡張しました:

#!/bin/bash

parse_url() {
    local query1 query2 path1 path2

    # extract the protocol
    proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"

    if [[ ! -z $proto ]] ; then
            # remove the protocol
            url="$(echo ${1/$proto/})"

            # extract the user (if any)
            login="$(echo $url | grep @ | cut -d@ -f1)"

            # extract the Host
            Host="$(echo ${url/$login@/} | cut -d/ -f1)"

            # by request - try to extract the port
            port="$(echo $Host | sed -e 's,^.*:,:,g' -e 's,.*:\([0-9]*\).*,\1,g' -e 's,[^0-9],,g')"

            # extract the uri (if any)
            resource="/$(echo $url | grep / | cut -d/ -f2-)"
    else
            url=""
            login=""
            Host=""
            port=""
            resource=$1
    fi

    # extract the path (if any)
    path1="$(echo $resource | grep ? | cut -d? -f1 )"
    path2="$(echo $resource | grep \# | cut -d# -f1 )"
    path=$path1
    if [[ -z $path ]] ; then path=$path2 ; fi
    if [[ -z $path ]] ; then path=$resource ; fi

    # extract the query (if any)
    query1="$(echo $resource | grep ? | cut -d? -f2-)"
    query2="$(echo $query1 | grep \# | cut -d\# -f1 )"
    query=$query2
    if [[ -z $query ]] ; then query=$query1 ; fi

    # extract the fragment (if any)
    fragment="$(echo $resource | grep \# | cut -d\# -f2 )"

    echo "url: $url"
    echo "   proto: $proto"
    echo "   login: $login"
    echo "    Host: $Host"
    echo "    port: $port"
    echo "resource: $resource"
    echo "    path: $path"
    echo "   query: $query"
    echo "fragment: $fragment"
    echo ""
}

parse_url "http://login:[email protected]:8080/one/more/dir/file.exe?a=sth&b=sth#anchor_fragment"
parse_url "https://example.com/one/more/dir/file.exe#anchor_fragment"
parse_url "http://login:[email protected]:8080/one/more/dir/file.exe#anchor_fragment"
parse_url "ftp://[email protected]:8080/one/more/dir/file.exe?a=sth&b=sth"
parse_url "/one/more/dir/file.exe"
parse_url "file.exe"
parse_url "file.exe#anchor"
1
Lisias

Bash 3.0以上にアクセスできる場合は、再照合演算子=~のおかげで、純粋なbashでもこれを行うことができます。

pattern='^(([[:alnum:]]+)://)?(([[:alnum:]]+)@)?([^:^@]+)(:([[:digit:]]+))?$'
if [[ "http://[email protected]:3142" =~ $pattern ]]; then
        proto=${BASH_REMATCH[2]}
        user=${BASH_REMATCH[4]}
        Host=${BASH_REMATCH[5]}
        port=${BASH_REMATCH[7]}
fi

外部プロセスが生成されないため、前のすべての例よりも高速でリソースの消費が少ないはずです。

1
Adam Ryczkowski

完全なURLからドメインのみを取得する単純なアプローチ:

echo https://stackoverflow.com/questions/6174220/parse-url-in-Shell-script | cut -d/ -f1-3

# OUTPUT>>> https://stackoverflow.com

パスのみを取得します。

echo https://stackoverflow.com/questions/6174220/parse-url-in-Shell-script | cut -d/ -f4-

# OUTPUT>>> questions/6174220/parse-url-in-Shell-script

2番目のコマンドは前のスラッシュを削除するため、完璧ではありません。手動で追加する必要があります。

ここでのみパスを取得するために、awkベースのバージョンがあります。

echo https://stackoverflow.com/questions/6174220/parse-url-in-Shell-script/59971653 | awk -F"/" '{ for (i=4; i<=NF; i++) printf"/%s", $i }'

# OUTPUT>>> /questions/6174220/parse-url-in-Shell-script/59971653
0
ccpizza

Bash 文字列操作を使用できます。習得は簡単です。正規表現で問題を感じる場合は、試してください。 NAUTILUS_SCRIPT_CURRENT_URIからのものであるため、そのURIにポートがある可能性があります。だから私はそれもオプションのままにしました。

#!/bin/bash

#You can also use environment variable $NAUTILUS_SCRIPT_CURRENT_URI
X="sftp://[email protected]/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
0

Node.jsにアクセスできる場合:

export MY_URI=sftp://[email protected]/some/random/path
node -e "console.log(url.parse(process.env.MY_URI).user)"
node -e "console.log(url.parse(process.env.MY_URI).Host)"
node -e "console.log(url.parse(process.env.MY_URI).path)"

これは出力します:

user
Host.net
/some/random/path
0
Brady Holt