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