web-dev-qa-db-fra.com

Extraire le nom de fichier et le chemin d'accès de l'URL dans le script bash

Dans mon script bash, je n'ai besoin d'extraire que le chemin de l'URL donnée ... Par exemple, à partir de la variable contenant la chaîne:

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

Je veux extraire à une autre variable que le:

/one/more/dir/file.exe

partie. Bien sûr, login, mot de passe, nom de fichier et paramètres sont facultatifs.

Depuis que je suis nouveau à sed et awk je vous demande de l'aide. S'il vous plaît, conseillez-moi comment le faire. Je vous remercie!

22
Arek

En 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%%\?*}"

Fonctionne uniquement si l'URL commence par http:// ou un protocole de même longueur Sinon, il est probablement plus facile d'utiliser regex avec sed, grep ou cut.

29
saeedgnu

Bash contient des fonctions intégrées pour gérer cela, par exemple, les opérateurs de correspondance de modèle de chaîne:

  1. '#' supprime les préfixes de correspondance minimaux
  2. '##' supprime les préfixes de correspondance maximaux
  3. '%' supprime les suffixes de correspondance minimaux
  4. '%%' supprime les suffixes de correspondance maximaux

Par exemple:

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

Tout cela dans l'excellent livre "Un guide pratique sur les commandes, les éditeurs et la programmation shell sous Linux" de Mark G. Sobell (http://www.sobell.com/)

71
JESii

Ceci utilise bash et cut comme un autre moyen de le faire. C'est moche, mais ça marche (du moins pour l'exemple). Parfois, j'aime utiliser ce que j'appelle des tamis couper pour réduire l'information que je recherche réellement. 

Remarque: Performance sage, cela peut être un problème.

Compte tenu de ces mises en garde:

D'abord, faisons écho à la ligne:

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

Ce qui nous donne:

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

Puis, coupons la ligne située au @ comme un moyen pratique de supprimer les http: // login: mot de passe:

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

Cela nous donne ceci:

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

Pour supprimer le nom d’hôte, faisons un autre cut et utilisons le / comme délimiteur en demandant à cut de nous donner le deuxième champ et tout ce qui suit (essentiellement, jusqu’à la fin de la ligne). Cela ressemble à ceci:

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

Ce qui entraîne à son tour:

un/plusieurs/dir/fichier.exe? a = sth & b = sth

Et enfin, nous voulons éliminer tous les paramètres de la fin. Encore une fois, nous allons utiliser cut et cette fois, le ? comme délimiteur et dites-lui de ne nous donner que le premier champ. Cela nous amène à la fin et ressemble à ceci:

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

Et le résultat est:

un/plus/dir/fichier.exe

Une autre façon de le faire et cette approche sont un moyen de réduire de manière interactive les données dont vous n’avez pas besoin de manière interactive pour trouver ce dont vous avez besoin.

Si je voulais insérer cela dans une variable dans un script, je ferais quelque chose comme ceci:

#!/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}

J'espère que ça aide.

6
Jim

Le fragment de code Perl est intriguant et, comme Perl est présent dans la plupart des distributions Linux, il est très utile, mais ... Il ne fait pas le travail complètement. Plus précisément, la traduction du format d'URL/URI d'UTF-8 en chemin Unicode pose un problème. Permettez-moi de donner un exemple du problème. L'URI d'origine peut être:

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

Le chemin correspondant serait:

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

%20 est devenu l'espace, %C3%A9 est devenu 'é'. Existe-t-il une commande Linux, une fonctionnalité bash ou un script Perl capable de gérer cette transformation, ou dois-je écrire une énorme série de substitutions de sous-chaînes sed? Qu'en est-il de la transformation inverse, du chemin d'accès à l'URL/URI?

(Suivre)

En regardant http://search.cpan.org/~gaas/URI-1.54/URI.pm , j'ai d'abord vu la méthode as_iri, mais cela manquait apparemment dans mon Linux (ou n'est pas applicable, en quelque sorte) . Il s'avère que la solution consiste à remplacer la partie "-> chemin" par "-> fichier". Vous pouvez ensuite décomposer cela en utilisant basename et dirname, etc. La solution est la suivante:

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

Bizarrement, utiliser "-> dir" au lieu de "-> fichier" n'extrait PAS la partie de répertoire: il formate plutôt l'URI de sorte qu'il puisse être utilisé comme argument de mkdir et autres.

(Suite de suivi)

Une raison pour laquelle la ligne ne peut pas être raccourcie à cela?

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

rester bouche bée

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="/"

sortie

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

Si vous avez un gawk:

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

ou

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

Gnu awk peut utiliser une expression régulière comme séparateur de champs (FS).

2
Hirofumi Saito

Comment ça:?

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

Pour obtenir d'autres parties d'URL, vérifiez: Obtenir des parties d'une URL (Regex) .

1
kenorb

En utilisant uniquement les commandes internes bash:

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

Ce que cela fait est:

  1. supprimez le préfixe *://*/ (il s'agirait donc de votre protocole et de votre nom d'hôte + port)
  2. vérifier si nous avons réussi à supprimer quoi que ce soit - sinon, cela implique qu'il n'y a pas de troisième barre oblique (en supposant qu'il s'agit d'une URL bien formée)
  3. s'il n'y a pas de troisième barre oblique, le chemin est simplement /

note: les guillemets ne sont pas vraiment nécessaires ici, mais je trouve plus facile de lire avec eux dans

1
caldfir

Le meilleur choix est de trouver une langue disposant d'une bibliothèque d'analyse d'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' )

ou

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

J'ai écrit une fonction pour extraire n'importe quelle partie ou l'URL. Je l'ai seulement testé en bash. Usage: 

url_parse <url> [url-part]

exemple: 

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

code:

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

Je conviens que "couper" est un outil merveilleux sur la ligne de commande. Cependant, une solution plus purement bash consiste à utiliser une fonctionnalité puissante d’expansion variable dans bash. Par exemple:

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