Académique Documents
Professionnel Documents
Culture Documents
Références
– Polycopié unix : chapitres (4), 5, (6), 7, 8, 9, 10
– Transparents unix : sections 4.3, (7), 8, 9 à 13
Créer un répertoire te3 sous votre répertoire ~/mni/unix : on y placera tous les fichiers de travail du TE3.
En particulier, on y copiera tous les fichiers utiles fournis dans le répertoire te3 du compte de l’UE.
Solution
cd mni/unix/te2
2. Sans changer de répertoire de travail, listez le contenu du répertoire M1 qui se trouve dans le
répertoire d’accueil de l’utilisateur lefrere.
Solution
ls ~lefrere/M1
3. Que font chacune des commandes suivantes ?
cd ; cd ~ ; cd .. ; cd /
Solution
Respectivement : aller dans le répertoire d’accueil (pour les deux premières), dans le répertoire
parent du répertoire courant, et aller à la racine du système.
4. Copiez le fichier ~lefrere/M1/foo.bar dans votre répertoire unix/te2
Solution
cp ~lefrere/M1/foo.bar ~/mni/unix/te2
5. À partir de votre répertoire d’accueil, donnez la commande permettant de renommer un fichier
foo.bar situé dans le répertoire ~/mni/unix/te2 en hello.txt (sans le changer de répertoire).
Solution
mv ~/mni/unix/te2/foo.bar ~/mni/unix/te2/hello.txt
6. Créez le répertoire ~/mni-test puis déplacez-le dans votre répertoire unix.
Solution
mkdir ~/mni-test ; mv ~/mni-test ~/mni/unix/
7. Listez les fichiers dont l’extension est .txt et qui sont situés dans votre répertoire te2, en affichant
leurs attributs (taille, droits d’accès, ...).
Solution
ls -l ~/mni/unix/te2/*.txt
16
UPMC M1 : MNI Correction du TE 3. Redirections, tubes et filtres
8. Quelle est la commande qui permet de localiser tous les fichiers toto.txt dans l’arborescence sous
votre répertoire d’accueil ?
Solution
find ~/ -name toto.txt -print
9. À la commande ls -l ~lefrere/M1/toto.out, vous obtenez la réponse suivante :
-rw-rw-r-- 1 lefrere personnel 4886 oct 1 2007 toto.out
Vous faites partie du groupe « etudiant ».
Pouvez-vous lire ce fichier ? Le modifier ? Le copier dans votre répertoire d’accueil ?
Solution
Ce fichier est lisible et modifiable par le propriétaire (lefrere), le groupe « personnel », et
uniquement lisible pour les autres utilisateurs. Faisant partie du groupe « etudiant », vous pouvez
donc le lire et le copier dans votre répertoire d’accueil, mais vous ne pouvez le modifier.
10. Dans un répertoire, on dispose d’une série de 9 fichiers nommés ftn.n où n est un entier qui
varie de 1 à 9. Comment supprimer les 7 fichiers ftn.2 à ftn.8 en une seule commande à un seul
argument sans risquer d’en détruire d’autres (ftn.1 ou ftn.f90 par exemple) ?
Solution
rm ftn.[2-8]
11. Indiquer les trois fonctions principales de la commande tar en précisant l’option associée à chaque
fonction.
Solution
création d’archive (c)
liste du contenu d’une archive (t)
extraction de l’arborescence (x)
#!/bin/sh
# fichier redir-out.sh
# le 20/09/2002
date > memo
hostname >> memo
whoami >> memo
cat /etc/motd >> memo
echo fin du memo >> memo
exit
1 cat ligne-ascii.txt
2 tr aeiou AEIOU < ligne-ascii.txt
3 tr aeiou AEIOU < ligne-ascii.txt > LIGNE-ascii.txt
4 cat LIGNE-ascii.txt
5 cat ligne-ascii.txt | tr aeiou AEIOU
6 tr ’0123456789’ ’abcdefghij’ < ligne-ascii.txt
7 tr ’0-9’ ’a-j’ < ligne-ascii.txt
8 tr "[:lower:]" "[:upper:]" < ligne-ascii.txt
9 tr "a-z" "A-Z" < ligne-ascii.txt
10 tr ’0-9’ ’+’ < ligne-ascii.txt
11
Solution
Dans l’ordre :
cat ligne-ascii.txt donne
abcdefghijklmnopqrstuvwxyz azerty 01234567890123
tr aeiuo AEIOU < ligne-ascii.txt ainsi que cat LIGNE-ascii.txt donnent
AbcdEfghIjklmnOpqrstUvwxyz AzErty 01234567890123
tr ’0123456789’ ’abcdefghij’ < ligne-ascii.txt
et tr ’0-9’ ’a-j’ < ligne-ascii.txt donnent
abcdefghijklmnopqrstuvwxyz azerty abcdefghijabcd
tr "[:lower:]" "[:upper:]" < ligne-ascii.txt
et tr "a-z" "A-Z" < ligne-ascii.txt donnent
ABCDEFGHIJKLMNOPQRSTUVWXYZ AZERTY 01234567890123
tr ’0-9’ ’+’ < ligne-ascii.txt donne
abcdefghijklmnopqrstuvwxyz azerty ++++++++++++++
Septembre 2008 avec mandriva 2007.1 en UTF-8 que ne gère pas tr : une version simplifiée sans caractères
accentués permet d’éviter les problèmes de codages ISO-8859/UTF-8. Mais on peut proposer de traiter
les caractères accentués par un contournement avec passage en ISO-8859-1 qui, lui, est très bien compris
par tr à condition de positionner les locales correctement. Par exemple :
echo éàç | \
iconv -f UTF-8 -t ISO-8859-1 | \
LC_ALL=fr_FR.ISO-8859-1 tr ’[[:lower:]]’ ’[[:upper:]]’ | \
iconv -f ISO-8859-1 -t UTF-8
On remarque que les caractères accentués situés après l’ascii standard dans l’ordre lexicographique ne
sont pas inclus dans l’intervalle a-z.
Pour des raisons historiques (bsd et SV), on trouve sur les systèmes:
- osf1 et aix : deux commandes "tr" (SV) et "trbsd" (bsd)
- linux : la commande "tr" avec une option supplémentaire "-t"
Pour la commande "trbsd" les crochets ne sont jamais considérés comme spéciaux
et les classes "[:alpha:]" ou les équivalences "[=e=]" sont inconnues;
si l’ensemble des caractères par lesquels on traduit est plus petit que celui
des caractères à traduire, il y a duplication automatique du dernier caractère
pour aboutir à deux ensembles de même longueur.
Cette duplication automatique fonctionne aussi par défaut sous linux
avec le commande "tr".
Au contraire, avec la commande "tr" sous osf1 et aix ou "tr -t" sous linux,
si l’ensemble d’arrivée comporte moins de caractères que celui de départ,
il y a troncature de celui de départ: il n’y a pas duplication automatique.
En résumé, il faut donc comparer "tr -t" sous linux avec "tr" osf1 ou aix
et "tr" sous linux avec "trbsd" sous osf1 et aix.
De plus, avec "tr" sous linux et "trbsd" les crochets "[]" sont des
caractères normaux... mais bien sûr s’ils sont présents des deux côtés
ils n’auront aucune influence : tr ’a-z’ ’A-Z’ est identique à
tr ’[a-z]’ ’[A-Z]’
Quand les crochets ont ils un caractère spécial dans la version System V ?
- pour étendre l’ensemble d’arrivée, il faut utiliser le multiplicateur "*"
mais à l’intérieur des crochets.
tr ’a-z’ ’[+*]’ (osf1/aix)
- pour accéder aux classes de caractères et aux classes d’équivalence
tr ’[:lower:]’ ’[:upper:]’ (osf1/aix) minuscules -> majuscules
tr ’[=e=]’ ’[e*]’ (osf1/aix) eéèêëEÉÈÊË -> e (si LANG ad hoc)
------------------------------------------------------------------------
Essais effectués avec LC_CTYPE et LC_COLLATE non francisés
------------------------------------------------------------------------
[1] [2]
------------------------------------------------------------------------
tr ’a-z’ ’+’ [abcd0z]
Linux (-t) [+bcd0z] tr [++++0+]
OSF1 [+bcd0z] trbsd [++++0+]
AIX [+bcd0z] trbsd [++++0+]
------------------------------------------------------------------------
tr ’a-z’ ’+*’ [abcd0z]
Linux (-t) [+*cd0z] tr [+***0*]
OSF1 [+*cd0z] trbsd [+***0*]
AIX [+*cd0z] trbsd [+***0*]
------------------------------------------------------------------------
------------------------------------------------------------------------
tr ’[a-z]’ ’[+*]’ [abcd0z]
Linux (-t) +++++0++ tr +++++0++
OSF1 +++++0++ trbsd [+*]]0]]
AIX +++++0++ trbsd [+*]]0]]
------------------------------------------------------------------------
tr ’a-z’ ’[+*]’ [abcd0z]
Linux (-t) [++++0+] tr [++++0+]
OSF1 [++++0+] trbsd [[+*]0]]
AIX [++++0+] trbsd [[+*]0]]
Ex. 5 : Tubes et filtres (more, less, wc, head, tail, sort, grep) A 25 min.
Pour chaque question de 1 à 7 utiliser une commande de liste via \ls 2 et un tube avec un filtre unix.
1. Afficher la liste des fichiers du répertoire /usr/bin/ en contrôlant le défilement écran par écran
(tester avec more puis avec less).
2. Compter le nombre de fichiers de ce répertoire (wc).
3. Afficher les noms des 8 derniers fichiers de /usr/bin/ (tail)
4. Afficher les noms des 8 premiers fichiers de /usr/bin/ (head)
5. Afficher les noms des fichiers de /usr/bin/ classés par ordre alphabétique inverse (sort).
6. Afficher la liste des fichiers du répertoire ~lefrere/M1/Config/ Compter le nombre des fichiers de
ce répertoire.
7. Afficher la liste des fichiers du répertoire ~lefrere/M1/Config/ avec leurs attributs ; les classer
par ordre de taille décroissante. Compter le nombre de fichiers classés. Pourquoi ne retrouve-t’on
pas le nombre précédent ? Insérer un filtre pour éviter ce problème.
On s’intéresse maintenant à des fichiers d’entête pour le préprocesseur cpp
8. Afficher les lignes du fichier /usr/include/limits.h qui comportent la chaîne MAX (grep). Compter
leur nombre.
9. Afficher les 3 premières lignes du fichier /usr/include/limits.h qui comportent la chaîne MAX.
10. AB Afficher les lignes du fichier /usr/include/limits.h qui comportent à la fois l’instruction
préprocesseur #define en début de ligne (avec un nombre arbitraire de blancs entre # et define)
et la chaîne MAX
Solution
#!/bin/sh
echo liste des fichiers de ’/usr/bin/’
\ls /usr/bin/ | more
# sous linux, on peut utiliser less
echo nombre de fichiers de ’/usr/bin/’
\ls /usr/bin/ | wc -l
\ls /usr/bin/ | awk ’END{print NR}’
echo liste des 8 derniers fichiers de ’/usr/bin/’
\ls /usr/bin/ | tail -n 8
echo liste des 8 premiers fichiers de ’/usr/bin/’
\ls /usr/bin/ | head -n 8
ls /usr/bin/ | awk ’NR<=10’
echo liste des fichiers de ’~lefrere/M1/Config’ et nombre de fichiers
\ls ~lefrere/M1/Config | wc -w
2. La commande \ls invoque la commande native /bin/ls évitant ainsi l’affichage des caractères *, @ ou / en fin de
nom de fichier que provoquerait l’alias sur ls -F.
\ls ~lefrere/M1/Config | wc -l
echo fichiers de ’~lefrere/M1/Config’ + attributs par ordre de taille décroissante
\ls -l ~lefrere/M1/Config | grep -v ’^total’ | sort -k 5,5nr
echo affichage de la taille et du nom des fichiers de ’~lefrere/M1/Config’
\ls -l ~lefrere/M1/Config | grep -v ’^total’ | awk ’{print $5, $NF}’
\ls -l ~lefrere/M1/Config | awk ’NR > 1 {print $5, $NF}’
echo nombre total d\’octets occupés par les fichiers de ’~lefrere/M1/Config’
\ls -l ~lefrere/M1/Config | grep -v ’^total’ | awk ’{s+=$5} END{print s}’
\ls -l ~lefrere/M1/Config | awk ’NR > 1 {s+=$5} END{print s}’
\ls -l ~lefrere/M1/Config | awk ’NF>=9 {s+=$5} END{print s}’
\ls -l ~lefrere/M1/Config | awk ’$1 != "total" {s+=$5} END{print s}’
#
echo "lignes de /usr/include/limits.h comportant la chaîne MAX"
grep MAX /usr/include/limits.h
echo "leur nombre"
grep MAX /usr/include/limits.h | wc -l
echo "3 premières lignes de /usr/include/limits.h comportant la chaîne MAX"
grep MAX /usr/include/limits.h | head -n 3
echo ’lignes de /usr/include/limits.h comportant ’# define ’ et ’MAX’
grep MAX /usr/include/limits.h | grep ’# *define’
#!/bin/sh
# traitement de l’image météo
int main(void)
{
float a[3], a2[3];
int i;
/* attention : lecture sans contrôle de validité */
for (i=0; i<3; i++)
{
scanf("%f", &a[i]);
}
for (i=0; i<3; i++)
{
a2[i] = a[i] * a[i];
}
for (i=0; i<3; i++)
{
printf("%12.9f ", a2[i]);
/* format fixé pour obtenir le même affichage
* qu’avec le format libre du fortran */
}
printf("\n");
exit(0);
}
3. Les filtres du système unix ne sont pas les seuls à pouvoir utiliser les redirections : les programmes
qui lisent au clavier et écrivent à l’écran acceptent aussi les redirections.
Saisir trois nombres dans le fichier in et faire afficher leurs carrés.
Solution
a.out < in
4. Quelles commandes permettent de stocker les résultats de plusieurs exécutions successives dans
un fichier out. Où est envoyé le message d’erreur dans le cas d’une saisie non numérique avec
l’exécutable fortran ? Comment le stocker dans un fichier nommé erreur ?
Solution
a.out > out
a.out >> out
L’erreur s’affiche à l’écran, sauf si on redirige la sortie d’erreur.
a.out 2> erreur >> out
5. AB Comment calculer les puissances quatrièmes des entrées sans modifier le programme source ?
6. B Comment récupérer aussi les carrés dans un autre fichier ?
Solution
a.out | a.out
Si on souhaite voir le résultat intermédiaire, on peut utiliser la commande tee. tee duplique le flux
d’entrée sur la sortie standard et sur le fichier qui lui est passé en paramètre (un T en termes de
tubes !). En choisissant le fichier spécial /dev/tty (qui représente l’écran), on peut donc afficher le
flux passé en pipe :
a.out | tee /dev/tty | a.out
De même, si on calcule les puissances huit des entrées, les résultats intermédiaires, puissances 2 et
4, peuvent être interceptés dans un fichier grâce à l’option -a (append) de tee.
a.out | tee interm | a.out | tee -a interm | a.out
– les deux premières lignes de ce fichier sont un en-tête que l’on ne modifiera pas : la première indique le
type de fichier (P1 pour pbm ascii), la seconde la géométrie de l’image (nombre de lignes et de colonnes) ;
– les lignes suivantes correspondent aux pixels noirs (1) ou blancs (0) de l’image parcourue de gauche à
droite puis de haut en bas ; chaque ligne comporte au plus 70 caractères.
Copier ce fichier depuis le compte de l’UE. Afficher l’image qu’il représente avec display ou xv par
exemple. On souhaite modifier ce fichier texte pour afficher le caractère & en blanc sur fond noir, via
différentes méthodes :
1. avec un éditeur de texte interactif ;
2. avec le filtre sed ;
3. avec le filtre tr et d’autres filtres élémentaires.
Solution
1. sous vi
3,$s/0/2/g
3,$s/1/0/g
3,$s/2/1/g
2. sed -e ’3,$s/0/2/g’ -e ’3,$s/1/0/g’ -e ’3,$s/2/1/g’ esperluette.pbm
autre solution : sed -e ’3,$y/01/10/’ esperluette.pbm
3. avec tr sur le corps de l’image seulement (découper et recoller)
tail -n +3 esperluette.pbm | tr 01 10 > corps
head -n 2 esperluette.pbm | cat - corps > tmp
mv tmp esperluette.pbm
Création de bitmaps à partir des polices cmr de tex
cd /usr/local/exports/latex/TEXLive6b/texmf/fonts/pk/ljfour/public/cm
pktopbm cmr10.3761pk -c38 -x 380 /tmp/jal/esperluette-.pbm
pour le -x, voir avec xv puis bouton milieu de la souris pour afficher le numéro du pixel
pnmtopnm -plain /tmp/jal/esperluette-.pbm > /tmp/jal/esperluette.pbm
pour passer en ascii 0/1
Compléments du TE 3
Ex. 10 : Trace d’une matrice carrée (awk) A 15 min.
Le fichier mat5-5.dat stocke une matrice carrée 5 par 5. Sans faire d’hypothèse mat5-5.dat
11 12 13 14 15
sur la taille de la matrice, écrire les programmes awk qui calculent :
21 22 23 24 25
1. la somme des éléments de sa deuxième colonne ; 31 32 33 34 35
2. la somme des éléments de sa troisième ligne 41 42 43 44 45
51 52 53 54 55
3. AB sa trace (somme des éléments diagonaux).
Solution
1. {s+=$2} END{print s}
2. NR==3 {for (i=1;i<=NF;i++) {s+=$i} ; print s; exit} exit facultatif
3. {s+=$NR} END{print s}
1 program trinome
2 !
3 ! résolution de l’équation du second degré
4 ! mise à jour le 13/10/2005
5 !
6 ! déclaration des variables
7 implicit none
8 real :: a, b, c
9 real :: x1, x2
10 real :: delta
11 real :: q
12 ! saisie des données
13 write(*,*) ’résolution de l’’équation du binôme ax^2+bx+c=0’
14 write (*,*) ’entrer les trois coefficients a, b, c’
15 read (*,*) a,b,c
16 write (*,*) ’a=’, a, ’ b=’, b, ’ c=’, c
17
36 ! x2=(-b-sqrt(delta))/(2.*a)
37 ! => sommer des termes de même signe
38 !
39 ! sign(u,v)=+|u| si v>0
40 ! -|u| si v<0
41 !
42 q=-(b+sign(sqrt(delta),b) ) / 2.
43 x1=q/a
44 x2=c/q
45 write (*,*) ’ x1=’, x1, ’ x2=’, x2
46 endif
47 else
48 ! degré un ou zéro
49 ! il vaudrait mieux tester abs(b) > tiny(b)
50 if (b/=0.) then
51 ! degré un
52 write (*,*) ’ degré un’
53 x1=-c/b
54 write (*,*) ’une solution unique’
55 write (*,*) ’ x=’, x1
56 else
57 ! degré zéro
58 write (*,*) ’ degré zéro’
59 ! il vaudrait mieux tester abs(c) > tiny(c)
60 if (c/=0.) then
61 write (*,*) ’pas de de solution’
62 else
63 write (*,*) ’infinité de solutions’
64 endif
65 endif
66 endif
67 stop
68 end program trinome
1. Faire une copie de sauvegarde du fichier trinome.f90.
2. Créer un fichier constitué des lignes 23 à 46 de trinome.f90.
Solution
head -n 46 trinome.f90 | tail -n 24
sed -n -e ’23,46p’ trinome.f90
3. Créer un fichier ne contenant que les lignes de commentaires de trinome.f90. On rappelle qu’en
fortran 90, une ligne est un commentaire si son premier caractère non-blanc est un point d’excla-
mation (!).
Solution
grep ’^ *!’ trinome.f90
sed -n -e ’/^ *!/p’ trinome.f90
4. Créer un fichier omettant tous les commentaires de trinome.f90
Solution
grep -v ’^ *!’ trinome.f90 ou
grep ’^ *[^ !]’ trinome.f90 différences liées aux lignes vides
sed -n -e ’/^ *[^ !]/p’ trinome.f90
5. Ajouter des blancs après les virgules.
Solution
sed -e ’s/,/, /g’ trinome.f90
:%s/,/, /g sous vi
Solution
awk -f abscisses-depenses.awk depenses | \
sort -k 9,9n | awk ’{s+=$7; print $9, $7,s}’ > dettes.dat
où abscisses-depenses.awk est le script-awk suivant
{
jour=$2 +30*$3 + 365*($4-2000)
print $0, jour
}
Sous awk, on pourrait compléter pour traiter les dates de façon plus réaliste (durée des mois, années
bissextiles).
Le script de visualisation utilise la redirection d’entrée pour gnuplot pour éviter de multiplier les fichiers
annexes :
#! /bin/sh
# représentation graphique de l’endettement
EOF
#
1. Utiliser la commande awk pour permuter les notes d’oral et d’examen (ex). Comment restituer le
séparateur « ; » ?
Solution
BEGIN{FS=";";OFS=";"}
{print $1,$2,$3,$5,$4}
Solution
BEGIN={";"; OFS=";"}
{tmp=$4; $4=$5; $5=tmp; print $0}
Solution
BEGIN={";"; OFS=";"}
{tmp=$4; $4=$5; $5=tmp; for (i=1; i<=NF; i++){print $i}}
2. Calculer la moyenne par épreuve sur l’ensemble du groupe d’étudiants et l’afficher.
Solution
BEGIN{FS=";"}
NR==1{ print $0; print "moyennes par épreuve"}
NR > 1 {
scc1+=$2
scc2+=$3
se+=$4
so+=$5
}
END{print "moyennes", scc1/(NR-1), scc2/(NR-1), se/(NR-1), so/(NR-1)}
Attention, sous awk les variables de langue pilotent la notation décimale : c’est LC_NUMERIC qui per-
met de passer du point décimal (si LC_NUMERIC=en_US par exemple) à la virgule (si LC_NUMERIC=fr_FR
par exemple). On peut le vérifier en positionnant ponctuellement la langue quand on lance la com-
mande, comme suit (sans données d’entrée) :
LC_NUMERIC=en_US awk --posix ’END{print 1/2}’ </dev/null donne 0.5
LC_NUMERIC=fr_FR awk --posix ’END{print 1/2}’ </dev/null donne 0,5
# moyenne2-notes.awk
BEGIN{FS=";"}
NR==1 {print $0,"Moyenne"}
NR > 1 {
moy=0
for (i=2;i<=NF;i++) moy+=$i
moy=moy/(NF-1)
print $0,moy
}
Solution
BEGIN{FS=" ";OFS=" "}
{print $2,$1,$3}
Solution
BEGIN={" "; OFS=" "}
{tmp=$1; $1=$2; $2=tmp; print $0}
Solution
Solution
s/\(.*\) \(.*\) \(.*\)/\2 \1 \3/
Solution
s/\([^ ]*\) \([^ ]*\) \([^ ]*\)/\2 \1 \3/
est nettement préférable à cause de l’avidité due au multiplicateur *. Il essaie d’avaler tout ce qu’il
peut et donc, si le délimiteur n’est pas explicitement exclus de l’ensemble des caractères constituant
un champ, le premier multiplicateur va absorber tous les délimiteurs qui ne sont pas explicitement
spécifiés sur l’expression recherchée.
6. B Reprendre la permutation nom–prénom avec awk puis sed sur le fichier notes1+blancs.dat.
Expliquer.
Solution
Les premières solutions avec awk et sed sont en défaut !
113 G
114 p
115 :1<RET> # <!>
116 Rintroduction <ESC> # mode remplacement
117 w
118 D
119 :q! # sortie forcee sans enregistrer les modifications
1 Initiation rapide à vi
2
54 4) Insertion
55 i, a : insère avant, après le curseur (i=insert, a=append)
56 I, A : insère en début, en fin de ligne
57 o, O : ouvre une ligne après, avant la ligne courante (o=open)
58
59 5) Modification
60 r : remplace un caractère sans passage en mode insertion (r=replace)
61 ~ : changement minuscule <-> majuscule sans passage en mode insertion
62
65 6) Mémorisation
66 yw : mémorise le mot courant (y=yank)
67 yy, Y : mémorise la ligne courante
68
76 8) Recherches
77 /motif : recherche le motif vers l’avant
78 ?motif : recherche le motif vers l’arrière
79 n, N : nouvelle recherche dans le même sens, en sens opposé (n=next)
80
81 9) Divers
82 . : répète la dernière commande d’édition
83 u : annule la dernière modification (u=undo)
84 Comportement différent entre vi et vim en cas de répétition
85 Attention: sous aix (ibm) u ramène le curseur sur le 1er caractère non blanc
86 U : restitue la ligne courante (annulant toutes les modifications
87 faites tant que le curseur n’a pas quitté la ligne)
88 % : si le curseur pointe sur un délimiteur ouvrant ([{, positionne
89 sur le délimiteur fermant associé )]} ou réciproquement
90 ===================================================================
91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++