#!/bin/bash
# Initialisation des variables pour les options
programme=$(basename $0)
substitut=$(echo $programme | sed 's/./ /g')
version='0.9.4'
modif='13/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 ] [ -i ]
$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