Un épisode sur la fonction
en bash
PLAN
INTRODUCTION
MÉTHODES
APPLICATION
RÉFÉRENCES
INTRODUCTION
Après un examen
rétrospectif de notre script, nous avons décidé de le reprogrammer dans
l’intention de 1) clarifier les chaînes de traitement ; 2) de simplifier
des codes répétitifs ; et 3) de récolter un corpus plus satisfaisant. Pour
ce faire, nous avons réorganiser des structures de contrôles, créé 19
fonctions, et amélioré des « algorithmes » de formalisation et
normalisation des textes.
Dans ce blog, nous nous concentrerons sur la création des
fonctions. Nous commencerons par une brève présentation pour ensuite expliquer
notre application. Quant à les objectifs 1) et 3) supra, nous les détaillerons
dans le prochain blog.
I METHODES
Considérons une fonction comme un petit script dans un script. Au lieu d’écrire le même code encore et encore, nous pouvons le définir une fois dans une fonction puis l’appeler à chaque fois. Elle est particulièrement utile si nous avons certaines tâches qui doivent être effectuées plusieurs fois.
1.1 Syntaxe
Il existe deux formats pour créer une fonction en bash.
nom_fonction () {
<commandes>
}
function nom_fonction () { <commandes> }
La définition de la fonction doit apparaître dans le
script avant tout appel.
1.2 Passer des arguments
A l’intérieur d’une fonction, nous pouvons accéder à des
arguments à l’aide des paramètres positionnels ($1, $2, $3, … , ${10}, $(n) ).
Attention : les nombres qui sont
supérieurs à 9 doivent être inclus dans une paire d’acclolade {}.
De plus, il faut considérer la portée de variables :
locale ou globale. Une variable locale n’est visible qu’à l’intérieur de la
fonction, tandis qu’une variable globale est visible partout dans le script.
1.3 Design
Créer des fonctions est facile. Créer de bonnes fonctions
qui facilitent l’écriture et la maintenance exige cependant du temps et de l’expérience...
Quelquefois, mieux est le moins de lignes de code,
parfois mieux est plus facile à modifier plus tard si les besoins changent.
L’approche qui est la moins sujette aux erreurs est parfois meilleure.
II APPLICATION
Nous avons créé 19 fonctions, dont 3 concernent l’écriture
des tableaux et 11 servent à générer des données ainsi que des fichiers
nécessaires ; le reste nous permet de formaliser/ normaliser des fichiers
et de manipuler des fichiers temporaires.
2.1 Écriture des tableaux
write_thead()
Fonction qui permet d'écrire des en-têtes (th) d'un tableau.
Pour créer n en-têtes, il faut appeler n fois la fonction
et passer 2 variables pour chaque appel.
$1 : la largeur
de la colonne en pourcentage (width) ;
$2 : le titre de
la colonne
write_thead () {
echo -e "\t\t\t\t<th width = \"$1\" align = \"center\">$2</th>";
}
write_cell ()
Fonction qui écrit le contenu de chaque case (cell) sur le stdOUT ; elle constitue la fonction write_line (). Une variable à passer pour chaque appel.
write_cell () {
echo -e "\t\t\t\t<td align = \"center\">$1</td>";
}
write_line ()
Fonction qui remplit le tableau ligne par ligne en appelant la fonction write_cell(). Dans notre script, il y a 14 variables à passer pour cette fonction : chacune d’entre elles correspond à une colonne. Notre tableau en comporte 14.
Les deux
variables $i et $j sont locales ; tandis que $COL_NUM est globale et constante.
$j est assignée par référence indirecte.
write_line () {
echo -e "\t\t\t<tr>";
local i=1;
local j;
while [ $i -le $COL_NUM ] ; do
eval j=\${$i};
write_cell "$j";
((i++));
done
echo -e "\t\t\t</tr>";
}
2.2 Récupération de
données et de fichiers nécessaires
get_remote_encoding ()
Fonction qui récupère l’encodage à l’aide de curl,
commande qui envoie au serveur une requête de l’en-tête.
La valeur est passée par substitution de commande.
$1 : à passer : $line : une URL
get_remote_encoding () {
curl -sIL $1|grep -i -Po '(?<=charset=).+(?=\b)' | \
awk '{print tolower($0)}';
}
get_page_encoding ()
Fonction qui récupère l’encodage
dans le balise <meta> d’une page HTML ;
La valeur est passée par substitution de commande.
$1 : à passer : $page : une page HTML aspirée
La valeur est passée par substitution de commande.
$1 : à passer : $page : une page HTML aspirée
get_page_encoding () {
egrep -i 'meta.+charset' $1 |awk '{print tolower($0)}' | \
egrep -o "charset[=\s\"a-Z0-9\-]*" |cut -d"=" -f2 | \
sed 's/\s//g'|sed 's/\"//g';
}
check_encoding ()
Fonction qui vérifie si l’encodage est reconnu par la
commande iconv.
La valeur est passée par substitution de commande. Si
celle-ci est vide, le fichier correspondant ne peut pas être transcodé par
iconv.
$1 : à passer : $encodage
check_encoding () {
iconv -l | egrep -io $1 | sort -u;
}
get_text ()
Fonction qui décharge la page HTML d’une URL
$1 : à passer : $encodage, l’encodage de la page
$2 : à passer : $line, une URL
Par défaut, la sortie est sur stdOUT
get_text () {
lynx -dump -nolist -assume_charset=%{charset} -display_charset=$1 $2;
}
get_context ()
Fonction qui produit le contexte d'un motif. Les --
lignes qui délimitent des contextes ainsi que des lignes vides sont enlevés.
$1 : à passer : nombre de ligne(s) à extraire avant/après
la ligne contenant le motif
$2 : à passer : le motif
$3 : à passer : un fichier texte
get_context () {
egrep -i -C $1 "$2" $3 | sed '/^--|^$/d';
}
get_context_html ()
Fonction qui produit le
contexte d’un motif à l’aide de minigrep et le déplace dans un répertoire
souhaité.
$minigrep, globale : le chemin du programme minigrep
$1 : à passer : un fichier dump (formatté)
$2 : à passer : nom du fichier de motif
$3 : à passer : $minigrep_out, le fichier html généré par minigrep
$4 : à passer : la destination du déplacement du fichier $3
$minigrep, globale : le chemin du programme minigrep
$1 : à passer : un fichier dump (formatté)
$2 : à passer : nom du fichier de motif
$3 : à passer : $minigrep_out, le fichier html généré par minigrep
$4 : à passer : la destination du déplacement du fichier $3
get_context_html () {
perl $minigrep "UTF-8" $1 $2;
mv $3 $4
}
get_frequency ()
Fonction qui calcule le nombre d’occurrences d’un motif
dans un fichier texte.
La valeur est passée par substitution de commande.
$1 : à passer : $motif
$2 : à passer : un fichier texte formatté avec la
fonction supra. Sinon, on risque de
perdre certaine occurrence à cause de « line wrap ».
get_frequency () {
egrep -io "$1" $2 | wc -l;
}
get_index ()
Fonction qui produit un index de tokens (formes) ou de
types (lemmes) par ordre de fréquence décroissante.
$1 : à passer : fichier de liste de tokens ( ou de types)
get_index () {
sort -bdfi $1 |uniq -ci | sort -gr;
}
get_2gram ()
Fonction qui crée une liste de 2gram d’un fichier de
liste de tokens
$1 : à passer : fichier de liste de tokens
get_2gram () {
temp1=$1;
local temp2=tp2;
tail -n +2 $temp1 > $temp2;
paste $temp1 $temp2 | get_index;
rm $temp2;
}
get_ttr ()
Fonction qui calcule le type/token ratio (ttr) d’un
certain fichier.
La valeur est passée par substitution de commande.
$type_nb et $token_nb : variables locales
$1 : à passer : fichier de l'index de lemmes
$2 : à passer : fichier de liste de tokens (ie, le « contexte »
des lemmes)
get_ttr () {
local type_nb=`cat $1 | wc -l`;
local token_nb=`cat $2 | wc -l`;
echo $(( 100 * type_nb / token_nb ));
}
concatenate ()
Fonction qui concatène des fichiers dans un seul fichier.
Comme les signes <, > sont enlevés dans les phases (fonctions)
précédentes, on ne les traite pas ici.
$1 : à passer : nom du balise, par exemple $line_iterator
$2 : à passer : nom du fichier à concaténer
concatenate () {
echo "<t=\"$1\">"
cat $2;
echo -e "\n</t>";
}
2.3 Formalisation/normalisation
de textes, manipulation de fichiers temporaires
format_latin_text ()
Fonction qui 1) nettoie un fichier composé de l'alphabet
latin en supprimant des « bruits », tels que des titres des
rubriques, des listes des articles récents, des noms des images, etc, qui sont
en général marqués par des caractères spéciaux ; 2) et le formatte de manière « syntaxique », c'est-à-dire que l’on touche le « line wrap »
et met chaque phrase par ligne.
$1 : à passer : un ficher texte, dans notre script, un
fichier dump utf-8 de l’anglais ou du français.
format_latin_text () {
sed -r "/[\*\+▪] |\*$|\#|\[.*\]|[=&]|IFRAME|__|©|(c|C)opyright|\
\( \)|(BUTTON)|http|www| \b[0-9]+\.|\.jpe?g|png|JPE?G|PNG|\
<.*[^>]>|[<>]|[a-Z0-9+=%]{30}|^$/d" $1 | tr '\n' ' '| \
tr -s ' ' | sed 's/[.?!]/&\n/g';
}
format_chinese_text ()
Fonction similaire, mais ne traite que des textes chinois
(sans délimiteur). Elle permet de 1) nettoyer et 2) formatter un texte, 3) et
de le segmenter avec le tokenizer scws (cf le blog précédent).
$1 : à passer : un ficher texte chinois encodé en utf-8
$dictionnaire_scws, globale : le chemin du dictionnaire du
tokenizer
format_chinese_text () {
sed -r "/[\*\+] |\[.*\]|[=&¿]|IFRAME|__|©|(c|C)opyright|\( \)|\
BUTTON|\||http|www| \b[0-9]+\.|\.jpe?g|png|JPE?G|PNG|<.*[^>]>|\
[<>]|^$/d" $1 | tr -d '\n' | \
tr -d ' ' |sed "s/[。?!]/&\n/g" | \
scws -c utf8 -d $dictionnaire_scws;
}
token_to_line ()
Fonction qui sépare des tokens par ligne. Quelque fois,
\w produit des bruits, on énumère donc bêtement des signes de ponctuations. On
garde des tokens de genre mots polylexicaux composés par des traits d'union, tels
que « chassé-croisé » ;
$1 : à passer : fichier (con)texte formatté, selon des
objectifs.
token_to_line () {
sed -r "s/[,.«»()\?\!…:;\"\‘\’*#。?!,、…+=%&:;【】\
· —“”–《》()<>_■€\$\¥£•@②]|--| - / /g" $1 | \
tr '\n' ' ' | tr ' ' '\n'| tr -s '\n'| sed "/^$/d" ;
}
lemmatize ()
Fonction qui lemmatize une liste de tokens avec
tree-tagger. Les nombres, marqués par NUM en français et CD en anglais, sont
enlevés.
$1 : à passer : fichier de liste de tokens à traiter
$2 : à passer : fichier paramètre du tree-tagger
lemmatize () {
sed -r "s/[’\']/\n/g" $1| tree-tagger $2 -lemma -no-unknown | \
sed -r "/NUM.+|CD.+/d" |sed "/^$/d" | cut -f2 ;
}
remove_stopwords ()
Fonction qui enlève des mots vides (stopwords) à partir
d'un antidictionnaire
-F interprète MOTIF comme une liste de chaînes fixées et
séparées par ligne
-f lit MOTIF depuis un fichier
-v sélectionne des « non-matching » lignes
-x sélectionne ceux qui « match » exactement la
ligne
$1 : à passer : fichier d’antidictionnaire (un token par
ligne)
$2 : à passer : fichier de liste de tokens à nettoyer (un
token par ligne)
remove_stopwords () {
grep -Fvxf $1 $2;
}
REFERENCES