web-dev-qa-db-ja.com

bashスクリプトのURLからファイル名とパスを抽出する

私のbashスクリプトでは、指定されたURLからパスのみを抽出する必要があります。たとえば、文字列を含む変数から:

http:// login:[email protected]/one/more/dir/file.exe?a = sth&b = sth

私は他の変数に抽出したいだけです:

/one/more/dir/file.exe

部。もちろん、ログイン、パスワード、ファイル名、パラメータはオプションです。

私はsedとawkが初めてなので、手助けを求めます。方法を教えてください。ありがとうございました!

24
Arek

Bashでは:

URL='http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth'
URL_NOPRO=${URL:7}
URL_REL=${URL_NOPRO#*/}
echo "/${URL_REL%%\?*}"

URLがhttp://または同じ長さのプロトコルで始まる場合にのみ機能します。それ以外の場合は、sedgrepまたはcut ...で正規表現を使用する方が簡単です。

30
saeedgnu

これを処理するためのbashには組み込み関数があります。たとえば、文字列のパターンマッチング演算子:

  1. '#'一致する最小限のプレフィックスを削除
  2. '##'一致する最大プレフィックスを削除
  3. '%'は最小限の一致するサフィックスを削除します
  4. '%%'は、最大一致サフィックスを削除します

例えば:

FILE=/home/user/src/prog.c
echo ${FILE#/*/}  # ==> user/src/prog.c
echo ${FILE##/*/} # ==> prog.c
echo ${FILE%/*}   # ==> /home/user/src
echo ${FILE%%/*}  # ==> nil
echo ${FILE%.c}   # ==> /home/user/src/prog

優れた本からのこれらすべて: "Mark G. SobellによるLinuxコマンド、エディター、およびシェルプログラミングの実践ガイド(http://www.sobell.com/)

76
JESii

これは別の方法としてbashcutを使用します。醜いですが、機能します(少なくとも例では)。時々、私はcutふるいを使用して、実際に探している情報を絞り込みます。

注:パフォーマンスに関しては、これが問題になる可能性があります。

それらの警告を考えると:

まず、行をエコーし​​ましょう:

echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth'

それは私たちに与えます:

http:// login:[email protected]/one/more/dir/file.exe?a = sth&b = sth

次に、cutの行を@で削除し、便利な方法でストリップしますhttp:// login:password

echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
cut -d@ -f2

それは私たちにこれを与えます:

example.com/one/more/dir/file.exe?a=sth&b=sth

ホスト名を取り除くには、別のcutを実行して/区切りとして、2番目のフィールド以降のすべてを(基本的には行の終わりまで)提供するようにカットに要求します。次のようになります。

echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
cut -d@ -f2 | \
cut -d/ -f2-

これにより、次の結果になります。

one/more/dir/file.exe?a = sth&b = sth

最後に、最後からすべてのパラメーターを取り除きます。繰り返しますが、cutを使用し、今回はを区切り文字を使用して、最初のフィールドのみを提供するように指示します。これで終わりになり、次のようになります。

echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
cut -d@ -f2 | \
cut -d/ -f2- | \
cut -d? -f1

そして出力は:

one/more/dir/file.exe

それを行うもう1つの方法であり、このアプローチは、必要のないデータをインタラクティブな方法で作成するために、不要なデータをインタラクティブに取り除く1つの方法です。

これをスクリプトの変数に入れたい場合は、次のようにします。

#!/bin/bash

url="http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth"
file_path=$(echo ${url} | cut -d@ -f2 | cut -d/ -f2- | cut -d? -f1)
echo ${file_path}

それが役に立てば幸い。

7
Jim
url="http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth"

GNU grep

$ grep -Po '\w\K/\w+[^?]+' <<<$url
/one/more/dir/file.exe

BSD grep

$ grep -o '\w/\w\+[^?]\+' <<<$url | tail -c+2
/one/more/dir/file.exe

ripgrep

$ rg -o '\w(/\w+[^?]+)' -r '$1' <<<$url
/one/more/dir/file.exe

URLの他の部分を取得するには、次を確認してください: RLの一部を取得(正規表現)

3
kenorb

Perlスニペットは興味深いものであり、PerlはほとんどのLinuxディストリビューションに存在するため、非常に便利ですが...完全には機能しません。具体的には、URL/URI形式をUTF-8からパスUnicodeに変換する際に問題があります。問題の例を挙げましょう。元のURIは次のとおりです。

file:///home/username/Music/Jean-Michel%20Jarre/M%C3%A9tamorphoses/01%20-%20Je%20me%20souviens.mp3

対応するパスは次のようになります。

/home/username/Music/Jean-Michel Jarre/Métamorphoses/01 - Je me souviens.mp3

%20がスペースになりました、%C3%A9は 'é'になりました。この変換を処理できるLinuxコマンド、bash機能、またはPerlスクリプトはありますか?または、sedサブストリング置換の膨大なシリーズを作成する必要がありますか?パスからURL/URIへの逆変換についてはどうですか?

(ファローアップ)

http://search.cpan.org/~gaas/URI-1.54/URI.pm を見て、最初にas_iriメソッドを見つけましたが、それは明らかにLinuxから欠落していた(または該当しません) 、 何とかして)。解決策は、「-> path」の部分を「-> file」に置き換えることです。次に、basenameやdirnameなどを使用して、さらに分解することができます。したがって、解決策は次のとおりです。

path=$( echo "$url" | Perl -MURI -le 'chomp($url = <>); print URI->new($url)->file' )

奇妙なことに、「-> file」の代わりに「-> dir」を使用してもディレクトリ部分は抽出されません。むしろ、mkdirなどの引数として使用できるようにURIをフォーマットします。

(さらなるフォローアップ)

行をこれに短縮できない理由は何ですか?

path=$( echo "$url" | Perl -MURI -le 'print URI->new(<>)->file' )
2
Urhixidur

ガウク

echo "http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth" | awk -F"/" '
{
 $1=$2=$3=""
 gsub(/\?.*/,"",$NF)
 print substr($0,3)
}' OFS="/"

出力

# ./test.sh
/one/more/dir/file.exe
2
ghostdog74

Gawkを使用している場合:

$ echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
  gawk '$0=gensub(/http:\/\/[^/]+(\/[^?]+)\?.*/,"\\1",1)'

または

$ echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
  gawk -F'(http://[^/]+|?)' '$0=$2'

GNU awkはフィールド区切り記号(FS)として正規表現を使用できます。

2
Hirofumi Saito

これはどうですか?

echo 'http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth' | \
sed 's|.*://[^/]*/\([^?]*\)?.*|/\1|g'
1
sed

Bashビルトインのみを使用:

path="/${url#*://*/}" && [[ "/${url}" == "${path}" ]] && path="/"

これは何ですか:

  1. 接頭辞*://*/を削除します(これがプロトコルとホスト名+ポートになります)
  2. 実際に何かを削除することに成功したかどうかを確認します。それ以外の場合は、3番目のスラッシュがないことを意味します(これは整形式のURLであると想定しています)
  3. 3番目のスラッシュがない場合、パスは/になります。

注:引用符は実際にはここでは必要ありませんが、一緒に読むと簡単です

1
caldfir

最善の策は、URL解析ライブラリを持つ言語を見つけることです。

url="http://login:[email protected]/one/more/dir/file.exe?a=sth&b=sth"
path=$( echo "$url" | Ruby -ruri -e 'puts URI.parse(gets.chomp).path' )

または

path=$( echo "$url" | Perl -MURI -le 'chomp($url = <>); print URI->new($url)->path' )
1
glenn jackman

任意の部分またはURLを抽出する関数を作成しました。私はbashでのみテストしました。使用法:

url_parse <url> [url-part]

例:

$ url_parse "http://example.com:8080/home/index.html" path
home/index.html

コード:

url_parse() {
  local -r url=$1 url_part=$2
  #define url tokens and url regular expression
  local -r protocol='^[^:]+' user='[^:@]+' password='[^@]+' Host='[^:/?#]+' \
    port='[0-9]+' path='\/([^?#]*)' query='\?([^#]+)' fragment='#(.*)'
  local -r auth="($user)(:($password))?@"
  local -r connection="($auth)?($Host)(:($port))?"
  local -r url_regex="($protocol):\/\/($connection)?($path)?($query)?($fragment)?$"
  #parse url and create an array
  IFS=',' read -r -a url_arr <<< $(echo $url | awk -v OFS=, \
    "{match(\$0,/$url_regex/,a);print a[1],a[4],a[6],a[7],a[9],a[11],a[13],a[15]}")

  [[ ${url_arr[0]} ]] || { echo "Invalid URL: $url" >&2 ; return 1 ; }

  case $url_part in
    protocol) echo ${url_arr[0]} ;;
    auth)     echo ${url_arr[1]}:${url_arr[2]} ;; # ex: john.doe:1234
    user)     echo ${url_arr[1]} ;;
    password) echo ${url_arr[2]} ;;
    Host-port)echo ${url_arr[3]}:${url_arr[4]} ;; #ex: example.com:8080
    Host)     echo ${url_arr[3]} ;;
    port)     echo ${url_arr[4]} ;;
    path)     echo ${url_arr[5]} ;;
    query)    echo ${url_arr[6]} ;;
    fragment) echo ${url_arr[7]} ;;
    info)     echo -e "protocol:${url_arr[0]}\nuser:${url_arr[1]}\npassword:${url_arr[2]}\nhost:${url_arr[3]}\nport:${url_arr[4]}\npath:${url_arr[5]}\nquery:${url_arr[6]}\nfragment:${url_arr[7]}";;
    "")       ;; # used to validate url
    *)        echo "Invalid URL part: $url_part" >&2 ; return 1 ;;
  esac
}
1
Mike

「カット」はコマンドラインのすばらしいツールであることに同意します。ただし、より純粋なbashソリューションは、bashで変数展開の強力な機能を使用することです。例えば:

pass_first_last='password,firstname,lastname'

pass=${pass_first_last%%,*}

first_last=${pass_first_last#*,}

first=${first_last%,*}

last=${first_last#*,}

or, alternatively,

last=${pass_first_last##*,}
1
Roger