#!/bin/bash # Initialisation des variables pour les options programme=$(basename $0) substitut=$(echo $programme | sed 's/./ /g') version='1.2.1' modif='10/11/2022' function usage { echo "Usage : $programme -d (fichier|-) -r (résultats|-) [ -f champs ]* " echo " $substitut [ -T taille ] [ -j cookie_jar ] [ -ixw ] " echo " $programme -h " } function aide { cat << EOT Usage ===== $programme -d (fichier|-) -r (résultats|-) [ -f champs ]* $substitut [ -T taille ] [ -j cookie_jar ] [ -ixw ] $programme -h Options ======= -d indique le nom du fichier avec la liste des DOIs à chercher. Si on a un tiret “-” en argument, la liste de DOIs est lue sur l’entrée standard -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” -r indique le nom du fichier résultat (pour la sortie standard, utiliser le tiret “-” comme argument de l’option) -T indique le nombre de DOI à chercher à chaque itération (90 par défaut, avec un maximum de 100) -x indique que les notices doivent être en XML au lieu de JSON, le format par défaut -w indique que la recherche se fait avec les identifiants WoS (“UT”) au lieu des DOI EOT exit 0 } # Déclaration explicite des tableaux “bases” et “champs” declare -a champs tag='DO' format='json' # Options while getopts d:f:hij:r:T:xw i do case $i in d) dois=$OPTARG;; f) champs+=$OPTARG;; h) aide;; i) recid=1;; j) cookiejar=$OPTARG;; r) sortie=$OPTARG;; T) taille=$OPTARG;; x) format='xml';; w) tag='UT';; \?) echo >&2 usage >&2 exit 1;; esac done # Vérification des options if [[ -z $dois || -z $sortie ]] then echo "Erreur : option(s) manquante(s)" >&2 echo "" >&2 usage >&2 echo "" exit 2 fi if [[ $dois = '-' ]] then dois='/dev/fd/0' elif [[ ! -f $dois ]] then echo "Erreur : fichier \"$dois\" introuvable" >&2 exit 3 fi if [[ $sortie = '-' ]] then sortie='/dev/fd/1' else touch $sortie fi if [[ -z $cookiejar ]] then cookiejar='cookies.txt' fi # Vérification de la présence des programmes # “curl” et “jq” 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 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 optionView='FS' 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 [[ $recid -gt 0 ]] then extra="$extra, \"optionOther\": \"RI+On\"" fi function nettoie { if [[ -f tmpWos$$.json ]] then rm -f tmpWos$$.json fi if [[ -f tmpWos$$.txt ]] then rm -f tmpDoi$$.txt fi } function requete { end=$(( $3 + $2 - 1 )) printf " -> %d - %d " $3 $end >&2 cat << EOT > search.json { "databaseId": "WOS", "lang": "en", "usrQuery": "$1", "count": 100, "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 cod=$(perl -ne 'print "$1" if /"code": "(.+?)"/o' $out) if [[ -n $cod ]] then msg="$cod: $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) if [[ $rec -gt 100 ]] then echo " " echo "Erreur : le nombre de réponses ($rec) est supérieur au maximum autorisé (100)" echo " " nettoie exit 7 fi total=$(( $total + $rec )) 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 printf " \r" >&2 } # Itéraion sur la liste des DOIs max=${taille:-90} delai=${WOS_DELAY:-1} nb=0 offset=1 total=0 # Vérification des variables “$max” et “$delai” 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 if [[ $delai -lt 1 ]] then echo "Erreur : la variable \$WOS_DELAY ne peut pas valoir moins d‘une seconde" exit 4 fi SECONDS=0 # Nettoyage des données d’entrée perl -pe 's/\x{EF}\x{BB}\x{BF}//go; s/\r//go;' $dois > tmpDoi$$.txt while read x do nb=$(( $nb + 1 )) if [[ $nb -eq 1 ]] then liste="\\\"$x\\\"" else liste="$liste OR \\\"$x\\\"" fi if [[ $nb -eq $max ]] then requete "$tag=($liste)" "$nb" "$offset" offset=$(( $offset + $nb )) liste="" nb=0 while [[ $SECONDS -lt $delai ]] do sleep 0.5 done SECONDS=0 fi done < tmpDoi$$.txt rm -f tmpDoi$$.txt if [[ $nb -gt 0 ]] then requete "$tag=($liste)" "$nb" "$offset" fi nb=$(( $offset + $nb - 1 )) 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 if [[ $? -eq 0 ]] then mv tmpWos$$.json $sortie else echo "Erreur détectée par “jq” : vérifiez les données dans le fichier \"tmpWos$$.json\"." >&2 fi else perl -ne 'foreach $rec (m|(<REC .+?</REC>)|go) { print "$rec\n"; }' $sortie | (echo '<records>'; cat; echo '</records>') | xmllint -format - > tmpWos$$.xml if [[ $? -eq 0 ]] then mv tmpWos$$.json $sortie else echo "Erreur détectée par “xmllint” : vérifiez les données dans le fichier \"tmpWos$$.json\"." >&2 fi fi amt=$(perl -ne 'print "$1" if /\bx-rec-amtperyear-remaining: (\d+)/o;' $err) printf " \r" >&2 echo " " >&2 echo "Nombre de DOIs cherchés : $nb" >&2 (echo "Nombre de notices trouvées : $total / $top notices" echo "Forfait restant : $amt" ) | perl -pe '1 while(s/(\d)(\d\d\d)\b/$1 $2/o);' >&2 exit 0