#!/bin/bash # Initialisation des variables pour les options programme=$(basename $0) substitut=$(echo $programme | sed 's/./ /g') version='0.5.3' modif='25/07/2022' function usage { echo "Usage : $programme -q queryId -n nb_notices -r (résultats|-) [ -o offset ] " 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 queryId -n nb_notices -r (résultats|-) [ -o offset ] $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] $substitut [ -j cookie_jar ] [ -ix ] $programme -h Options ======= -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) -h affiche cette aide -i ajoute dans la réponse de l’API la liste des identifiants WoS des notices retournées -j indique le nom du fichier qui recevra les cookies (“cookies.txt” par défaut) -n indique le nombre total de notices WoS à télécharger (la valeur maximale est donnée par le programme envoyant la requête à l’API WoS) -o indique à partir de quel numéro (offset), on veut télécharger les notices, en particulier pour reprendre un téléchargement après un problème de connexion (1 par défaut) -q indique le numéro de la requête “queryId” dont on veut télécharger les résultats -r indique le nom du fichier résultat (pour la sortie standard, utiliser le tiret “-” comme argument de l’option) -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 -x indique que les notices doivent être en XML au lieu de JSON, le format par défaut N.B. : la variable d‘environnement WOS_DELAY permet de modifier le délai entre deux itérations (1 seconde par défaut). Sa valeur doit être un entier positif supérieur à 1, par ex. : “export WOS_DELAY=3” EOT exit 0 } # Déclaration explicite des tableaux “bases” et “champs” declare -a champs declare -a tri format='json' # Options while getopts f:hij:n:o:q:r:t:x i do case $i in f) champs+=$OPTARG;; h) aide;; i) recid=1;; j) cookiejar=$OPTARG;; n) nrec=$OPTARG;; o) offset=$OPTARG;; q) qid=$OPTARG;; r) sortie=$OPTARG;; t) tri+=$OPTARG;; x) format='xml';; \?) echo >&2 usage >&2 exit 1;; esac done # Vérification des options if [[ -z $qid || -z $nb || -z $sortie ]] then echo "Erreur : option(s) manquante(s)" >&2 echo "" >&2 usage >&2 echo "" exit 2 fi if [[ ! $nb =~ ^[1-9][0-9]*$ ]] then echo "Erreur : le nombre de notices (option “-n”) doit être un entier positif non nul" >&2 exit 2 fi if [[ -z $offset ]] then offset=1 else if [[ $offset =~ ^[1-9][0-9]*$ ]] then if [[ $offset -gt $nb ]] then echo "Attention : offset supérieur au nombre de notices" >&2 fi else echo "Erreur : l’offset (option “-o”) doit être un entier positif non nul" >&2 exit 2 fi fi if [[ $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 # Vérification de la variable '$WOS_DELAY' if [[ -n $WOS_DELAY && ! $WOS_DELAY =~ ^[1-9][0-9]*$ ]] then echo "Erreur : la valeur de la variable \$WOS_DELAY doit être un entier supérieur à 1" >&2 exit 2 fi # 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' # 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 options="optionView=FS&viewField=$viewField" else options="optionView=FR" 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 if [[ -n $options ]] then options="$options&sortField=$sortField" else options="sortField=$sortField" fi fi if [[ $recid -gt 0 ]] then if [[ -n $options ]] then options="$options&optionOther=RI+On" else options="optionOther=RI+On" fi fi # Lancement de la requête function cherche { end=$(( $1 + $2 - 1 )) printf " -> %d - %d " $1 $end >&2 if [[ $3 = 'json' ]] then curl -X GET -b $cookiejar -c $cookiejar -v "$url/query/$qid?count=$2&firstRecord=$1&$options" \ -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 "" exit 6 fi # Test de la réponse msg=$(perl -ne 'print "$1" if /"message": "(.+?)"/o' $out) if [[ -n $msg ]] then cod=$(perl -ne 'print "$1" if /"code": "(.+?)"/o' $out) if [[ -n $cod ]] then msg="$cod: $msg" fi echo " " echo "Erreur : “$msg” " echo " " exit 5 else rec=$(perl -ne '$nb ++ if /"UID": /o;}{print $nb;' $out) cat $out >> $sortie echo $rec fi elif [[ $3 = 'xml' ]] then curl -X GET -b $cookiejar -c $cookiejar -v "$url/query/$qid?count=$2&firstRecord=$1&$options" \ -H "X-ApiKey: $key" -H "Accept: application/xml" 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 "" 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 " " exit 5 else rec=$(perl -ne 'foreach $r (m|(<REC )|go) {$nb ++;}}{print $nb;' $out) cat $out >> $sortie echo $rec fi fi } # Vérification de la variable “$max” max=${taille:-100} delai=${WOS_DELAY:-1} total=$(( $nb - $offset + 1 )) if [[ $max -lt 1 ]] then echo "Erreur : le nombre de notices par itération doit être compris entre 1 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 while [[ $total -gt 0 ]] do if [[ $total -lt $max ]] then max=$total fi SECONDS=0 rc=$(cherche "$offset" "$max" "$format") printf "\r \r" >&2 if [[ $rc -lt $max ]] then echo "Attention : $rc notices reçues pour $max notices attendues" >&2 fi total=$(( $total - $max )) offset=$(( $offset + $max )) notices=$(( $notices + $rc )) while [[ $total -gt 0 && $SECONDS -lt $delai ]] do sleep 0.5 done done printf "\r \r" >&2 if [[ $format = 'json' ]] then perl -ne 'if (not $m) { if (/^( +)\{/o) { $m = $1; if ($ok) { print ","; $ok = 0; } print; } } else { if (/^$m/o) { print; $n ++; } else { $ok ++ if $n; $m = "" if /^\{/o; } }' $sortie | (echo '{"Records": {"records": {"REC": ['; cat; echo ']}}}') | jq . > tmpWos$$.json mv tmpWos$$.json $sortie offset=$(perl -ne '$nb ++ if /"UID": /o;}{print $nb;' $sortie) else perl -ne 'foreach $rec (m|(<REC .+?</REC>)|go) { print "$rec\n"; }' $sortie | (echo '<records>'; cat; echo '</records>') | xmllint -format - > tmpWos$$.xml mv tmpWos$$.xml $sortie offset=$(perl -ne 'foreach $r (m|(<REC )|go) {$nb ++;}}{print $nb;' $sortie) fi amt=$(perl -ne 'print "$1" if /\bx-rec-amtperyear-remaining: (\d+)/io;' $err) echo " " >&2 (echo "Nombre de notices téléchargées : $notices notices" echo "Nombre total de notices : $offset notices" echo "Forfait restant : $amt" echo "") | perl -pe '1 while(s/(\d)(\d\d\d)\b/$1 $2/o);' >&2 exit 0