#!/bin/bash # Initialisation des variables pour les options programme=$(basename $0) substitut=$(echo $programme | sed 's/./ /g') version='0.9.5' modif='22/07/2022' function usage { echo "Usage : $programme -q 'requête' [ -r (résultats|-) ] [ -n nb_notices ] " echo " $substitut [ -[cmp] début:fin ] [ -l [0-9]+[DWMY] ] [ -b [WOS:]base ]* " echo " $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] " echo " $substitut [ -j cookie_jar ] [ -ix ] " echo " $programme -h " } function aide { cat << EOT Usage ===== $programme -q 'requête' [ -r (résultats|-) ] [ -n nb_notices ] $substitut [ -[cmp] début:fin ] [ -l [0-9]+[DWMY] ] [ -b [WOS:]base ]* $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] $substitut [ -j cookie_jar ] [ -ix ] $programme -h Options principales =================== -c limite la recherche dans la base de données aux dates de création des notices indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par exemple, “2000-01-01:2020-07-31”) -h affiche cette aide -l limite la recherche dans la base de données aux dates de chargement des notices indiquées sous la forme d’un nombre de jours (D), de semaines (W), de mois (M) ou d’années (Y). Les valeurs acceptées vont de “0D” à “6D”, de “1W” à “52W”, de “1M” à “12M” et de “0Y” à “10Y” -m limite la recherche dans la base de données aux dates de modification des notices indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par exemple, “2000-01-01:2020-07-31”) -p limite la recherche dans la base de données aux dates de publication indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par exemple, “2000-01-01:2020-07-31”) -r indique le nom du fichier où placer les notices téléchargées (mettre un tiret pour indiquer la sortie standard) -q fournit la requête à envoyer (entre simples ou doubles quotes si elle contient des espaces ou des caractères spéciaux) -x indique que les notices doivent être en XML au lieu de JSON, le format par défaut Autres options ============== -b limite la recherche à une ou plusieurs bases (e.g. “WOS:SCI”). Cette option est répétitive et “WOS:” est facultatif (par défaut, la recherche se fait sur l’ensemble du WoS) -f limite les champs présents dans les notices téléchargées. Cette option est répétitive (par défaut, tous les champs sont présents) -i ajoute dans la réponse de l’API la liste des identifiants WoS des notices retournées (5 par défaut ou valeur de l’option “-n”) -j indique le nom du fichier qui recevra les cookies (“cookies.txt” par défaut) -n indique le nombre de notices, entre 0 et 100, à retourner (5 par défaut) -t trie les résultats sur un champ (nom abrégé à 2 majuscules) de façon croissante (A) ou décroissante (D) ou par pertinence (RS) forcément décroissante N.B. : on ne peut utiliser au maximum qu’une seule des options “-c”, “-l”, “-m” ou “-p” à la fois, sinon cela entraîne une erreur. EOT exit 0 } function nettoie { if [[ -f search$$.json ]] then rm -f search$$.json fi } # Déclaration explicite des tableaux “bases” et “champs” declare -a bases declare -a champs declare -a tri format='json' # Options while getopts b:c:f:hij:l:m:n:p:q:r:t:x i do case $i in b) bases+=($OPTARG);; c) cdate=$OPTARG;; f) champs+=($OPTARG);; h) aide;; i) recid=1;; j) cookiejar=$OPTARG;; l) ldate=$OPTARG;; m) mdate=$OPTARG;; n) nrec=$OPTARG;; p) pdate=$OPTARG;; q) query=$OPTARG;; r) sortie=$OPTARG;; t) tri+=($OPTARG);; x) format='xml';; \?) echo >&2 usage >&2 exit 1;; esac done # Vérification des options if [[ -z $query ]] then echo "Erreur : option(s) manquante(s)" >&2 echo "" >&2 usage >&2 echo "" exit 2 fi if [[ -z $sortie ]] then sortie='/dev/null' elif [[ $sortie = '-' ]] then sortie='/dev/fd/1' fi if [[ -z $cookiejar ]] then cookiejar='cookies.txt' fi # Vérification de la présence des programmes # “curl”, “jq” et “xmllint” for i in curl jq xmllint do prog=$(which $i 2> /dev/null) if [[ -z $prog ]] then echo "" >&2 echo "Erreur : programme “$i” introuvable. Vérifier \$PATH." >&2 echo "" >&2 exit 3 fi done # Définition des fichiers de sortie et de l’URL de l’API out="WosApiOut.$format" err='WosApiErr.txt' url='https://wos-api.clarivate.com/api/wos' # Vérification de la variable “$max” max=${nrec:-5} if [[ $max -lt 0 ]] then echo "Erreur : le nombre de notices par itération doit être compris entre 0 et 100" >&2 exit 4 elif [[ $max -gt 100 ]] then echo "Erreur : le nombre de notices par itération ne peut dépasser 100" >&2 exit 4 fi # Initialisation de la clé if [[ -z $key ]] then read -t 60 -s -p "clé d‘authentification : " key echo if [ $? -gt 128 ] then echo "" >&2 echo "Erreur : temps de réponse (60 s) dépassé " >&2 exit 3 fi fi # Gestion des autres options if [[ ${#champs[@]} -gt 0 ]] then viewField='WOS' for item in ${champs[@]} do while [[ -n $item ]] do deb=${item%%,*} fin=${item#*,} viewField="$viewField+$deb" if [[ $deb = $fin ]] then item="" else item=$fin fi done done extra="\"optionView\": \"FS\", \"viewField\": \"$viewField\"" else extra="\"optionView\": \"FR\"" fi if [[ ${#bases[@]} -gt 0 ]] then for item in ${bases[@]} do while [[ -n $item ]] do deb=${item%%,*} fin=${item#*,} if [[ $deb =~ ^[A-Z]+:[A-Z]+$ ]] then coll=${deb%:*} edit=${deb#*:} else coll='WOS' edit=$deb fi if [[ -n $edition ]] then edition="$edition,$coll+$edit" else edition="$coll+$edit" fi if [[ $deb = $fin ]] then item="" else item=$fin fi done done extra="$extra, \"edition\": \"$edition\"" fi if [[ -n $cdate || -n $mdate || -n $pdate ]] then if [[ -n $cdate ]] then if [[ -n $ldate || -n $mdate || -n $pdate ]] then erreur=$(( $erreur + 1 )) else label='createdTimeSpan' option='-c' span=$cdate fi fi if [[ -n $mdate ]] then if [[ -n $ldate || -n $pdate ]] then erreur=$(( $erreur + 1 )) else label='modifiedTimeSpan' option='-m' span=$mdate fi fi if [[ -n $pdate ]] then if [[ -n $ldate ]] then erreur=$(( $erreur + 1 )) else label='publishTimeSpan' option='-p' span=$pdate fi fi if [[ $erreur -gt 0 ]] then echo " " >&2 echo "Erreur : présence de plusieurs options indiquant une période" >&2 echo " " >&2 usage >&2 echo " " >&2 exit 4 fi regex="[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" if [[ $span =~ ^$regex:$regex$ ]] then begin=${span%:*} end=${span#*:} # Vérification de la validité de chaque date for i in $begin $end do day=$(date -d $i 2> /dev/null) if [[ -z $day ]] then echo "" echo "Erreur : date “$i” invalide" >&2 echo " " >&2 exit 4 fi done extra="$extra, \"$label\": \"$begin+$end\"" else echo " " >&2 echo "Erreur : argument incorrect pour l’option “$option”" >&2 echo " " >&2 exit 4 fi fi if [[ -n $ldate ]] then if [[ $ldate =~ ^([0-6]D|([1-9]|[1-4][0-9]|5[0-2])W|([1-9]|1[0-2])M|([0-9]|10)Y)$ ]] then extra="$extra, \"loadTimeSpan\": \"$ldate\"" else echo "" echo "Erreur : argument incorrect pour l’option “-l”" >&2 echo " " >&2 exit 4 fi fi if [[ ${#tri[@]} -gt 0 ]] then for item in ${tri[@]} do while [[ -n $item ]] do deb=${item%%,*} fin=${item#*,} if [[ ! $deb =~ ^(AU|CF|CG|CW|CV|LC|LD|PG|PY|SO|VL):[AD]$ && ! $deb =~ ^(RS|TC):D$ ]] then echo "Erreur : argument incorrect pour pour l’option “-t”" >&2 exit 4 fi nom=${deb%%:*} sens=${deb#*:} if [[ -n $sortField ]] then sortField="$sortField,$nom+$sens" else sortField="$nom+$sens" fi if [[ $deb = $fin ]] then item="" else item=$fin fi done done extra="$extra, \"sortField\": \"$sortField\"" fi if [[ $recid -gt 0 ]] then extra="$extra, \"optionOther\": \"RI+On\"" fi # Nettoyage en cas d’arrêt prématuré trap nettoie HUP INT QUIT TERM # Lancement de la requête requete="${query//\"/\\\"}" cat << EOT > search$$.json { "databaseId": "WOS", "lang": "en", "usrQuery": "$requete", "count": $max, "firstRecord": 1, $extra } EOT if [[ $format = 'json' ]] then curl -X POST -d @search$$.json -b $cookiejar -c $cookiejar -v "$url" \ -H "Content-Type: application/json" \ -H "X-ApiKey: $key" 2> $err | jq . 2>> $err > $out cr=$? echo "Code retour “jq” = $cr" >> $err if [[ $cr -gt 0 ]] then echo "" echo "Erreur : la réponse de l‘API est incorrecte [code jq = $cr]" >&2 echo "" nettoie exit 6 fi # Test de la réponse msg=$(perl -ne 'print "$1" if /"message": "(.+?)"/o' $out) if [[ -n $msg ]] then code=$(perl -ne 'print "$1" if /"code": "(.+?)"/o' $out) if [[ -n $code ]] then msg="$code: $msg" fi echo " " echo "Erreur : “$msg” " echo " " nettoie exit 5 else qid=$(perl -ne 'print "$1" if /^ +"QueryID": (\d+)/o;' $out) if [[ -n $qid ]] then rec=$(perl -ne 'print "$1" if /^ +"RecordsFound": (\d+)/o;;' $out) top=$(perl -ne 'print "$1" if /^ +"RecordsSearched": (\d+)/o;;' $out) cat $out >> $sortie fi fi elif [[ $format = 'xml' ]] then curl -X POST -d @search$$.json -b $cookiejar -c $cookiejar -v "$url" \ -H "Content-Type: application/json" \ -H "Accept: application/xml" \ -H "X-ApiKey: $key" 2> $err | xmllint -format - 2>> $err > $out cr=$? echo "Code retour “xmllint” = $cr" >> $err if [[ $cr -gt 0 ]] then echo "" echo "Erreur : la réponse de l‘API est incorrecte [code xmllint = $cr]" >&2 echo "" nettoie exit 6 fi # Test de la réponse msg=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|<message>(.+?)</message>|;' $out) if [[ -n $msg ]] then code=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|<code>(.+?)</code>|;' $out) if [[ -n $code ]] then msg="$code: $msg" fi echo " " echo "Erreur : “$msg” " echo " " nettoie exit 5 else qid=$(perl -ne 'print "$1" if m|<val name="QueryID">(\d+)</val>|;' $out) if [[ -n $qid ]] then rec=$(perl -ne 'print "$1" if m|<val name="RecordsFound">(\d+)</val>|;' $out) top=$(perl -ne 'print "$1" if m|<val name="RecordsSearched">(\d+)</val>|;' $out) cat $out >> $sortie fi fi fi amt=$(perl -ne 'print "$1" if /\bx-rec-amtperyear-remaining: (\d+)/io;' $err) echo " " >&2 echo "Identifiant de la requête : $qid" >&2 (echo "Nombre de notices trouvées : $rec / $top notices" echo "Forfait restant : $amt" echo "") | perl -pe '1 while(s/(\d)(\d\d\d)\b/$1 $2/o);' >&2 nettoie exit 0