Hadoop MapReduce en 5 min

Hadoop (i.e. High-availability distributed object-oriented platform) est une système distribué permettant de stocker et d’analyser des données. Le grand intérêt d’Hadoop est de proposer un framework pour effectuer des analyses de données de façon parallélisée sur plusieurs machines. D’autre part, Hadoop permet d’utiliser des machines normales et de les associer en groupe de façon à ce que les puissances de calcul soient additionnées. Ces groupes de machines sont appelés des clusters et chaque machine est appelée un nœud (ou node). En effet, pour effectuer de gros traitements il n’est pas nécessaire d’utiliser de gros serveurs de type “mainframe”, plusieurs PC individuels de puissance classique suffisent.

Les avantages principaux d’Hadoop sont:

  • Permet une grande scalabilité pour stocker et traiter de grandes quantités de données.
  • Est tolérant aux pannes.
  • Est optimisé pour un type varié de données (comme le texte ou les images).
  • Possède un écosystème riche et souvent gratuit composé de différents outils capables de résoudre une problématique précise.

Les traitements effectués par Hadoop sont appelés des jobs, ils sont exécutés suivant un paradigme unique appelé MapReduce. Ce paradigme a pour but de rendre l’implémentation d’un job moins complexe pour le développeur en l’affranchissant de toutes les problématiques liées au calcul distribué.

Ainsi un job MapReduce va effectuer plusieurs opérations:

  • Séparer les données en entrée en plusieurs morceaux indépendants (appelés chunks) qui pourront être traités en parallèle. Les chunks en entrée sont traités par des tâches appelées “map tasks”.
  • La sortie des tâches ou “map tasks” est ensuite triée.
  • Les données d’entrée et de sortie d’un job MapReduce sont stockées sur un système de fichiers distribués appelé HDFS (i.e. Hadoop distributed file system).
  • Le framework Hadoop permet de programmer l’exécution des tâches suivant la puissance de calcul, de monitorer l’exécution des tâches et de ré-exécuter les tâches dont l’exécution aurait échouée.
  • Un job MapReduce peut être implémenté sans effectuer de développement relatif à la parallélisation des tâches, à la distribution du travail sur les différentes machines du système ou des opérations de récupération des données sur le système de fichiers distribués. Il n’y a pas non plus de changements de l’implémentation vis-à-vis de l’adaptation du cluster à la charge.

Le but de cet article est d’expliciter les grandes lignes de l’architecture d’Hadoop, des différents éléments qui composent ce système et d’expliquer brièvement comment implémenter un job MapReduce.
Cette article permet d’avoir quelques connaissances théoriques pour permettre d’exécuter des jobs Hadoop MapReduce. Il n’y a pas d’explications de code mais juste des explications du point de vue algorithmique. Le code permettant d’exécuter un job en .NET sera explicité, par la suite, dans un article futur.

L’écosystème Hadoop

Comme indiqué plus haut, l’écosystème Hadoop est très riche et est composé plusieurs outils qui peuvent être organisé en couches. Les composants dans une même couche ne communiquent pas entre eux, ils communiquent avec des composants sur les couches supérieures ou inférieures:

  • Hadoop HDFS (i.e. Hadoop Distributed File System)
  • Yarn (i.e. Yet Another Resource negotiator): gestion de ressources.
  • MapReduce: modèle de programmation pour permettre d’appliquer un calcul (Map) et d’agréger les résultats (Reduce).
  • Hive et Pig: modèle de programmation haut niveau au dessus de MapReduce pour apporter une abstraction: Hive permet d’effectuer des requètes SQL et Pig permet d’effectuer du scripting sur des flux de données.
  • Giraph: permet d’analyser des graphes pour les réseaux sociaux.
  • Storm, Spark, Flink: traitement en mémoire et en temps réel.
  • HBase, Cassandra, MongoDB: base de données NoSQL permettant de gérer des collections de clé-valeurs ou de grandes tables séparées (i.e. “sparse tables”).
  • Zookeeper: permet de centraliser la configuration, la synchronisation et la disponibilité de ces outils.


Dans l’installation de base d’Hadoop, seuls les composants HDFS, Yarn et MapReduce sont présents. Les autres composants doivent etre installés de façon séparée.
Tous ces outils sont libres et peuvent être utilisés ensemble.

Des solutions existent pour livrer l’ecosystème entier avec tous les outils sans considérer des installations séparées comme Cloudera, MapR ou Hortonworks.
Ces solutions doivent être installées sur un système hôte ou sur des machines virtuelles. Il est aussi possible d’utiliser un écosystème entier avec des solutions Cloud comme Amazon EC2, HDInsight de Microsoft Azure ou Cloud Dataproc de Google Cloud.

Pour effectuer des tests, on peut aussi facilement installer Hadoop sur une Debian GNU/Linux en utilisant une machine virtuelle.

HDFS

HDFS (i.e. Hadoop Distributed File System) est un système de fichiers dont la caractéristique principale est de permettre à Hadoop d’utiliser des fichiers en entrée pour l’exécution de ses jobs et d’écrire le résultat des jobs de façon distribuée. La manipulation des fichiers devient ainsi complètement transparente.

Hadoop est un système distribué et nécessite de pouvoir traiter des fichiers sur plusieurs nœuds d’un cluster. HDFS est capable de répondre aux exigences posées par Hadoop suivant plusieurs critères:

  • HDFS peut répartir des fichiers sur plusieurs machines en additionnant la taille de chaque nœud de façon à ne former qu’un seul espace de stockage.
  • HDFS répartit plusieurs fractions d’un fichier sur plusieurs nœuds ce qui permet de stocker des fichiers dont la taille dépasse la taille du disque d’un nœud. Les fractions de fichier ont des tailles fixes de 128 MO, ces fractions s’appellent des “chunks”.
  • Pour éviter les pertes de données dans le cas de crash d’un disque, HDFS réplique ses fichiers sur plusieurs nœuds d’un cluster. Il existe aussi des paramètres pour que HDFS prenne en compte les racks dans lesquels sont rangés les serveurs. Ainsi dans la réplication, il peut isoler encore davantage les données.
  • L’écriture et la manipulation des données par HDFS sont transparentes. Le système de fichiers apparaît comme un espace de stockage unique alors qu’il est composé de l’addition de plusieurs disques sur plusieurs nœuds du cluster.

Pour éviter un trop grand nombre d’échanges de données entre nœuds d’un cluster, Hadoop minimise les données qui doivent transiter d’un nœud à l’autre lors du traitement d’un job. Les traitements se font, en priorité, sur le nœud où se trouve les données. Le programme effectuant les traitements est déplacé sur le nœud où il sera exécuté car il est généralement beaucoup moins volumineux que les données qu’il manipule.

HDFS permet, en outre, de stocker de grandes quantités de données dans de larges datasets de façon scalable c’est-à-dire qu’il est possible d’augmenter l’espace disque en ajoutant des nœuds au cluster. HDFS distingue plusieurs types de nœuds: les name nodes et les data nodes.

Chaque fichier stocké dans HDFS possède des méta-données qui sont:

  • Le nom du fichier,
  • Les données de permission relatives à ce fichier: similaire aux permissions dans un système Unix.
  • La localisation des blocs relatifs à ce fichier sur les data nodes.

La taille des blocs de fichiers étant assez grande (128 MO par défaut), ce qui permet de réduire le nombre de blocs utilisés pour stocker un ficher. Moins le nombre de blocs est important et moins nombreux seront les méta-données associés à chaque fichier. D’autre part, les blocs de grande taille permettent de favoriser la lecture en continu des données sur un disque. Plus les données seront lues de façon continue sur les disques et plus un job MapReduce sera exécuté de façon efficace et rapide. A l’opposé, la lecture de fichiers de taille faible implique des accés répétés et succints à des disques pouvant se trouver sur des nœuds différents. Ce qui tend à diminuer les performances.

La répartition des données est uniforme sur les nœuds du cluster. Un composant appelé le balancer s’exécute régulièrement pour répartir les données sur les nœuds de façon uniforme dès que la différence est trop importante. Si on ajoute un nœud au cluster, le balancer s’exécute aussi pour répartir les données sur le nouveau nœud lors d’une phase de réallocation de blocs. Supprimer un nœuds d’un cluster correspond aussi à une opération particulière.

Sur le schéma suivant, on peut voir qu’il existe un name node par cluster. Les clients ajoutent ou suppriment des fichiers de façon transparente directement sur les data nodes en passant par HDFS. Pour exécuter un job, les clients s’adressent au name node qui sollicite les data nodes. La réplication se fait directement entre data nodes, il n’y a pas d’échanges de données entre le name node et les data nodes.

Sources: https://hadoop.apache.org/docs/r1.2.1/hdfs_design.html

On peut facilement requêter HDFS à l’aide d’instructions à la ligne commandes.

Name node

Il s’agit d’un service central (appelé aussi master node). Il a pour but de:

  • Gérer l’état du système de fichiers
  • Maintient l’arborescence du système de fichiers d’Hadoop et les méta-données associées aux fichiers. Les méta-données permettent d’indiquer où le fichier se trouve physiquement.
  • Le name node connaît les autres types de nœuds appelés data nodes où se trouvent les blocs permettant de stocker les fichiers.
  • Quand le client intervient sur le système de fichiers, il le fait sur les data nodes. Le name node effectue ensuite les demandes aux data nodes pour répondre aux requêtes du client.

Les méta-données associées à chaque fichier sont stockées sur le disque auprès du name node dans des fichiers edits_xxx et fsimage_xxx (se trouvant dans le répertoire de données de HDFS: <répertoire de données>/hd-data/tmp/dfs/name/current).

Les positions des blocs de fichier dans les data nodes sont stockées en mémoire (de façon à permettre un accès rapide) et sont reconstruites à chaque démarrage du name node (pendant une phase appelée “safe mode”). Ainsi plus le nombre de fichiers stockée dans HDFS est important et plus il y aura de positions stockées dans la mémoire du name node. L’utilisation de fichiers de grande taille permet de minimiser la quantité de meta-données stockée dans la mémoire du name node. Enfin la mémoire du name node peut être une limite s’il y a trop de meta-données à stocker.

Secondary name node

Ce nœud copie les méta-données se trouvant auprès du name node et vérifie l’état de fonctionnement du name node. Dans le cas où ce dernier présente une défaillance, le secondary name node est capable de prendre le relais en prenant la place du name node principal.

Le passage du secondary name node au name node n’est pas automatique. Il nécessite une opération manuelle.

Data node

Les fichiers sont stockés sur les nœuds de ce type. Ils assurent une fonction de stockage et permettent au name node d’accéder aux données quand une tâche le nécessite.

Quand un fichier est ajouté à HDFS, il est séparé en plusieurs blocs de taille fixe lors d’une étape de “partitionning” et chaque bloc est ensuite répliqué sur plusieurs data nodes du cluster (1 cluster est lui-même composé de plusieurs data nodes). Par défaut, les blocs mémoire font 128 MO et le facteur de réplication de ces blocs est de 3, toutefois ces valeurs sont configurables.

Les data nodes répondent aux différentes demandes du name node et utilise les méta-données fournies par le name node pour:

  • Créer des blocs
  • Supprimer des blocs
  • Répliquer des blocs

Le name node ne fait qu’indiquer des traitements à effectuer aux data nodes, il ne lit pas directement les données. Lors de copies de données, par exemple, les copies sont effectuées entre data nodes, elles ne transitent pas par le name node.

Le partitionning permet des accès parallèles aux blocs du fichier. La réplication réponds à plusieurs objectifs:

  • Tolérance aux pannes: s’il y a un défaut d’un nœud ou une coupure réseau, les données ne sont pas perdues et sont facilement retrouvables.
  • Performance: il est plus rapide d’exécuter un traitement sur des données si elles se trouvent déjà sur le nœud qui va effectuer le traitement.

L’inconvénient de la réplication est qu’elle nécessite plus d’espace de stockage.

YARN

YARN (i.e. Yet Another Resource Negotiator) est un composant qui se trouve au dessus de HDFS qui permet de gêrer l’exécution de jobs en parallèle. Il sert de base et est utilisé par d’autres applications comme MapReduce, Spark etc…

Comme pour HDFS, YARN est composé de plusieurs services ayant des fonctions spécifiques: le resource manager, le node manager, l’application master et le container.

Echanges entre les services lors d’une job MapReduce

D’une façon générale, YARN organise l’exécution d’un job MapReduce dans un cluster en minimisant le déplacement des données et l’utilisation de ressources pour que les performances soient maximisées. Les données nécessaires à l’exécution d’un job ne se trouvent pas forcément dans le nœud où l’exécution sera effectuée. Ainsi, le traitement se fait en priorité où les données se trouvent pour augmenter la bande passante entre les clusters.

Resource Manager

Ce service permet d’ordonnancer et de dispatcher les tâches sur le cluster Hadoop. Quand YARN reçoit un job à exécuter, c’est le resource manager qui reçoit le job et qui le dispatche le plus rapidement possible sur les autres nœuds du cluster. Le cluster doit être exploité au maximum de ses capacités lors de l’exécution du job. Quand le resource manager reçoit le job sous forme de package MapReduce, il crée un application manager (on verra par la suite la fonction de ce composant) qu’il va transmettre à un nœud pour qu’il puisse exécuter le job.

Pour que le resource manager puisse assurer son rôle de coordination des ressources du cluster, il reçoit pendant l’exécution du job le statut des nœuds et l’application master lui transmet des requêtes pour obtenir de la ressource. C’est le resource manager qui va assigner à l’application master les ressources nécessaires à l’exécution du job.

Node manager

Chaque nœud ou machine du cluster dispose d’un service appelé node manager qui va organiser les resources de la machine. Le node manager envoie périodiquement le statut du nœud au resource manager pour que ce dernier puisse répartir la charge efficacement suivant les ressources.

Application master

Cette application est créée par le resource manager pour exécuter un job. En fonction des statuts que les node managers transmettent au resource manager, ce dernier va affecter l’exécution de l’application master auprès d’un nœud. Pendant l’exécution du job, le resource manager reçoit les informations de statut du job grâce au node manager.

L’application master a pour but de négocier les ressources auprès du resource manager. Quand le job est terminé, l’application master indique au resource manager la fin de son exécution. Le nœud est alors désalloué par le resource manager pour qu’il soit de nouveau disponible pour un nouveau job.

Container

Un container est une ressource abstraite correspondant à une machine: cette notion regroupe la mémoire, le réseau et d’autres ressources de la machine. Les containers envoient à l’application master, les statuts d’exécution d’un job MapReduce.

MapReduce

MapReduce est à la fois un paradigme de développement de jobs pour Hadoop et il est aussi un composant d’Hadoop. Le composant Hadoop MapReduce permet d’organiser et d’exécuter des jobs en parallèle sur un système de fichiers distribués HDFS.

Plusieurs autres outils de l’écosystème Hadoop utilisent Hadoop MapReduce comme par exemple:

  • Hive qui propose une infrastructure de stockage et une syntaxe SQL pour la requêter.
  • Pig qui est un langage de flux de données et un framework d’exécution de job en parallèle.

L’exécution de jobs en parallèle nécessite la maitrise de notions complexes comme les sémaphores, la mémoire partagée entre threads, de locks etc… Hadoop MapReduce permet d’éviter de prendre en compte toutes ces notions pour se focaliser davantage sur l’implémentation du job au sens fonctionnel à condition de respecter le concept de programmation MapReduce. Ce paradigme permet à Hadoop d’exécuter ses tâches de façon isolée pour optimiser la scalabilité d’un cluster.

A cause du paradigme de programmation MapReduce, tous les traitements ne sont pas forcément possibles. Ainsi pour utiliser MapReduce, il faut que:

  • Les calculs soient parallélisables: si certaines opérations dépendent l’une de l’autre, il n’y aura pas d’intérêt à utiliser Hadoop et un job MapReduce.
  • Traiter de grands volumes de données: la mise en place des tâches d’exécution de l’opération Map peuvent prendre à elles-seules 1 min. Donc pour que l’exécution d’un job MapReduce soit efficace, il faut que le traitement soit effectué sur un grand nombre de données.
  • Le résultat final soit plus petit que l’ensemble de données de départ: dans le cas contraire, un job MapReduce n’est pas forcément pertinent. En effet un part du job consiste à agréger des valeurs donc s’il n’y a pas d’agrégation, une part de l’exécution de ce job est inutile.
  • Etre capable de convertir les données en liste de paire clé/valeur: cette contrainte peut être difficile à mettre en place mais est primordiale pour l’exécution du job MapReduce car la clé est la seule façon d’avoir un élément permettant, d’abord de ségréger différentes valeurs pour permettre un traitement parallélisable (durant les étapes Map) et ensuite la clé est encore utilisée pour agréger les valeurs (durant la ou les étapes Reduce).

Architecture d’Hadoop

Avant d’expliquer l’exécution d’un job MapReduce et maintenant que certaines notions ont été définies, on peut expliquer l’architecture d’un cluster Hadoop.

Comme on a pu le voir plus haut, Hadoop s’occupe de récupérer les données pour effectuer les traitements distribués, de distribuer les tâches et de gérer les erreurs. Un cluster Hadoop nécessite plusieurs nœuds pour être complètement fonctionnel:

  • Master nodes: il en faut 2, le premier en mode actif et l’autre en mode passif. Le 2e remplace le premier en cas de défaillance. Le master node contient les services suivants:
    • Le name node HDFS
    • Le resource manager YARN
  • Worker nodes: il en faut 3 au minimum. Les données sont ainsi répliquées 3 fois sur les 3 nœuds. On peut rajouter d’autres nœuds, plus le nombre de nœuds est important et plus les tâches s’exécuteront rapidement. Comme indiqué plus haut, les données ne se trouvent pas forcément dans le nœud dans lequel les jobs vont s’exécuter. YARN effectue le transfert d’un worker node à l’autre en minimisant les transferts de données pour augmenter les performances. Chaque worker node contient les services:
    • Le data node HDFS
    • Le node manager YARN

Synthèse des services principaux exécutés sur les nœuds

Le master node effectue la coordination et les worker nodes s’occupent du stockage et du traitement des données. Il n’est pas nécessaire d’avoir une configuration hardware particulière pour les worker nodes.

En cas d’échecs, plusieurs points de redondance sont assurés pour palier à une panne d’un master node ou d’un worker node:

  • Si un worker node est en échec: les données sont répliquées 3 fois donc en cas d’échec d’un worker node, le cluster est encore fonctionnel et les données ne sont pas perdues. Un mécanisme de heartbeat permet à YARN de savoir si un worker node est hors-ligne ou non. Si un worker node devient absent ou si seulement un disque d’un nœud crashe, les données sont copiées sur un autre worker node pour être sûr d’avoir 3 réplications des données.
  • Si le master node est en échec: on ne peut plus utiliser YARN donc le nœud doit être mis en mode passif et le 2e master node qui était en mode passif doit être mis en mode actif. Le mécanisme de passage du mode passif à actif n’est pas automatique, il doit se faire manuellement.

Explication générale d’un job MapReduce

MapReduce est basé sur des concepts de programmation fonctionnelle:

  • Des opérations Map: permet de lire les données en entrée et d’appliquer des opérations à toutes les données lues.
  • Une ou plusieurs opérations Reduce: qui agrège les résultats provenant des opérations Map et les écrit.

Map

Les opérations Map sont effectuées par un composant logiciel appelé le mapper. Concrètement il s’agit d’une classe dans laquelle il faut implémenter les fonctions qui effectueront le traitement suivant:

  • Récupérer des données d’entrée fournies par Hadoop (les données d’entrée font partie des paramètres d’entrée du job Hadoop). Dans le cas d’un fichier texte, c’est Hadoop qui lit le fichier et non le mapper. Les données d’entrée du mapper sont directement les lignes du fichier sous forme de chaînes de caractères.
  • Filtrer ces données si nécessaire,
  • Ranger les données sous forme de paires clé/valeur et
  • Renvoyer la liste de paires clé/valeur dans Hadoop.

Ainsi une instance du mapper s’exécute dans une tâche et sur un bloc de données. Différentes instances du mapper s’exécutent en parallèle. Le nombre de tâches Map dépend du nombre de données en entrée toutefois le bon niveau de parallélisme est atteint entre 10 et 100 tâches Map par nœud. A la fin de son exécution, le mapper produit des données intermédiaires qui ne sont pas le résultat final. Il renvoie ces données à Hadoop qui effectuera d’autres traitements.

C’est dans l’étape Map et dans l’implémentation du mapper qu’il faut choisir la granularité des données qui seront traitées par l’étape Map. Par exemple, dans le cas où on doit effectuer des opérations sur les lignes d’un fichier, c’est cette étape qui indiquera si on doit lire un mot à la fois, une ligne à la fois ou une lettre à la fois etc…
Si on décide de lire une ligne à la fois, une tâche consistant à effectuer un traitement sur une ligne ne doit pas avoir de liens avec la ligne précédente ou la ligne suivante. Sachant que les traitements sont faits en parallèle, ils ne peuvent pas dépendre l’un de l’autre.

Par défaut, les données d’entrée du mapper sont des lignes (TextInputFormat) mais il est possible de modifier ce format d’entrée en précisant un autre format d’entrée (KeyValueInputFormat, FileInputFormat ou SequenceFileInputFormat). Préciser un autre format d’entrée permet de changer un autre niveau de granularité des données. Par exemple à ce niveau, on pourrait traiter les fichiers un à un ou par paragraphe au lieu de les traiter par lignes.

Reduce

L’étape Reduce est composée de 3 sous-étapes: Shuffle, Sort et une étape Reduce à proprement parlé.

Shuffle

Cette étape est effectuée par Hadoop. Elle permet de récupérer les paires de clé/valeur obtenues à la suite de l’opération Map puis de les mixer et de les trier en les répartissant dans des shuffle nodes. Les shuffle nodes sont, en fait, les mêmes nœuds que ceux utilisés lors de l’étape Map toutefois lorsque les paires de clé/valeur sont obtenues, elles sont réaffectées à des nœuds pouvant être différents de ceux dont elles sont issues. L’étape de Shuffle effectue, ainsi, une réaffectation des paires clé/valeur en fonction des clés.

Les étapes de Shuffle commencent dès qu’une opération Map s’est terminée et elles s’exécutent en parallèle. Les étape Shuffle s’exécutent en même temps que les étapes Sort.

Sort

L’étape de Sort est aussi effectuée par Hadoop. Elle s’exécute de façon simultanée avec l’étape Shuffle. Elle consiste à trier les paires clé/valeur et à les répartir dans les shuffle nodes suivant leur clé. Ainsi les paires possédant la même clé seront rangées en priorité dans le même shuffle node.

Cette étape permet de préparer l’étape Reduce de façon à réduire sa charge de travail. Comme indiqué précédemment, l’étape de Sort est effectuée en même temps que l’étape Shuffle.

Etape shuffle et sort lors de l’exécution d’un job MapReduce

Reduce

Cette étape est effectuée par un composant logiciel appelé reducer. De la même façon que pour le mapper, le reducer est une classe dans laquelle des fonctions effectuent un traitement fonctionnel pour traiter les paires de clé/valeur de façon à les agréger.

En effet, après l’étape Sort, les données d’entrée de l’étape Reduce sont des listes de paires clé/valeur regroupées par clé. Le reducer devra donc:

  • Récupérer les paires de clé/valeur, ces paires sont fournies par Hadoop,
  • Agréger les valeurs ayant la même clé
  • Renvoyer la liste de paire clé/valeur réduite à Hadoop pour qu’il écrive sur le disque le résultat du job.

Sachant que l’étape de Reduce a consisté à agréger les valeurs ayant la même clé, le résultat comprendra moins de paires que le nombre de paires fournies en entrée. A l’issue de l’étape Reduce, on obtiendra une liste de paires avec la clé et des valeurs agrégées.

Par défaut, il n’y a qu’une seule étape Reduce pour le job même s’il y a plusieurs nœuds. Dans le cas d’une seule étape Reduce, il y aura un seul fichier résultat contenant toutes les valeurs agrégées.

Exemple de l’utilisation d’un seul reducer lors d’un job MapReduce

Par configuration, il est possible d’indiquer que l’on souhaite plusieurs étapes Reduce. Dans ce cas, les opérations Reduce seront effectuées en parallèle et il y aura autant de fichier résultat que d’opération Reduce. Même s’il y aura plusieurs fichiers contenant les résultats, avoir plusieurs étapes Reduce permet de paralléliser cette étape de façon à réduire son temps d’exécution.

Exemple de l’utilisation de plusieurs reducers lors d’un job MapReduce

A l’opposé, on peut ne pas effectuer d’étape de Reduce. Si elle n’est pas effectuée, le fichier résultat contiendra une liste de paire clé/valeur sans agrégation des valeurs.

Exemples de jobs MapReduce

Word count

Le job “word count” est l’exemple classique utilisé pour illustrer un job MapReduce. Il consiste à compter le nombre de mots dans un texte. Ce job est facile à mettre en oeuvre car il ne nécessite qu’un fragment de texte sous forme de fichier texte en entrée. Il n’y a, par exemple, pas d’appels à une base de données ou à d’autres services tiers qui pourraient compliquer l’implémentation de ce traitement.

D’autre part, le job “word count” se prête bien à l’exécution d’un job MapReduce car si on considère chaque ligne comme étant un élément d’entrée, on peut facilement paralléliser le comptage.

En entrée, on a donc un texte sous forme de fichier. Ce texte comporte un certain nombre de lignes, elles-mêmes composées de plusieurs mots. Hadoop va découper chaque ligne en blocs séparés qui donneront des entrées différentes pour les tâches Map.

Les étapes du job MapReduce sont les suivantes:

  • Map: chaque tâche Map reçoit une ligne. La fonction implémentant l’étape Map va considérer les mots comme étant des clés et elle va associer la valeur "1" à chacun des mots. On obtient donc des paires de clé/valeur avec un clé pour chaque mot et "1" étant la valeur de chaque paire.

    Ainsi si on considère la ligne:
    “Hadoop est un outil puissant toutefois cet outil a des limites qui le rendent moins puissant”

    A l’issue de l’étape Map, les paires clé/valeur obtenues sont:

    ("Hadoop"/1), ("est"/1), ("un"/1), ("outil"/1), ("puissant"/1), 
    ("toutefois"/1), ("cet"/1), ("outil"/1), ("a"/1), ("des"/1), ("limites"/1),
    ("qui"/1), ("le"/1), ("rendent"/1), ("moins"/1), ("puissant"/1)
    

    On peut remarquer que les mots "outil" et "puissant" sont répétés et chaque occurrence donne une paire différente.

    Lors de l’étape de Map, on peut filtrer les valeurs, par exemple, en ne gardant que les mots de plus de 3 caractères. Après avoir filtré et mis toutes les lettres en minuscule, la liste de paires devient:

    ("hadoop"/1), ("outil"/1), ("puissant"/1), ("toutefois"/1), 
    ("outil"/1), ("limites"/1), ("rendent"/1), ("moins"/1), ("puissant"/1).
    
  • Shuffle et Sort: à l’issue de cette étape, les paires sont regroupées suivant leur clé et réparties sur les différents nœuds du cluster.
    Par exemple, on peut avoir la répartition:

    • Node 1: ("hadoop"/1), ("limites"/1), ("moins"/1), ("outil"/1), ("outil"/1).
    • Node 2: ("puissant"/1), ("puissant"/1), ("rendent"/1), ("toutefois"/1).
  • Reduce: cette étape va agréger les valeurs suivant leur clé. Cette agrégation se fait en additionnant les valeurs ayant la même clé. On obtient donc après la phase Reduce:
    ("hadoop"/1), ("limites"/1), ("moins"/1), ("outil"/2), ("puissant"/2), 
    ("rendent"/1), ("toutefois"/1).
    

Le fichier de sortie du job contient donc la liste des mots avec le nombre d’occurrences.

Moteur de recherche

Ce job est un version simplifiée des traitements effectuées par un moteur de recherche. Elle consiste à associer un mot à des adresses URL. Lorsqu’une page est analysée, il en ressort des mots clés associés à des URL. De manière à ce que chaque mot puisse diriger vers les pages où ils apparaissent, il faut effectuer un traitement pour associer un mot à une liste d’URL.

Comme on l’a dit, l’étape d’analyse consiste à détecter des mots clés dans une page. Le résultat de ce traitement est une liste de mots avec une URL correspondant à la page où ce mot apparait.
L’exécution d’un job MapReduce permettrait d’associer un mot à une liste d’URL de façon à exploiter cette liste de paires. Les étapes du job sont:

  • Map: on considère les mots comme étant des clés d’une paire mot/url. L’étape Map va consister à extraire la liste de ces paires à partir d’une opération d’analyse des pages. Cette étape permet aussi de filtrer les mots non pertinents comme les conjonctions de coordination, les verbes trop usuels etc…
    A la suite de cette étape, on a donc une liste de paires mot/url, par exemple:
    (mot1/url1), (mot2/url2), (mot3/url3), (mot2/url1), (mot3/url4), (mot3/url5), (mot1/url6), (mot3/url1), (mot1/url1), (mot1/url6), (mot1/url7).

    On remarque que cette liste contient des doublons, que certains mots dirigent vers des URL différentes ou que des mots différents dirigent vers la même URL.

  • Shuffle et Sort: cette étape va consister à regrouper les paires par mot clé et à les répartir suivant les nœuds du cluster. Par exemple, on peut obtenir la répartition suivante:
    • Node 1: (mot1/url1), (mot1/url6), (mot1/url1), (mot1/url6), (mot1/url7)
    • Node 2: (mot2/url2), (mot2/url1), (mot3/url3), (mot3/url4), (mot3/url5), (mot3/url1)
  • Reduce: cette étape consiste à agréger chaque URL au mot auquel elle est associée. Pour chaque mot, on obtient une liste d’URL. Les doublons peuvent servir à associer une pertinence aux résultats de façon à les trier. A la suite de cette étape, on obtient pour chaque mot:
    • mot1: url1 (pertinence 2), url6 (pertinence 2), url7 (pertinence 1).
    • mot2: url2 (pert. 1), url1 (pert. 1).
    • mot3: url3 (pert. 1), url4 (pert. 1), url5 (pert. 1), url1 (pert. 1).

Ainsi quand on écrit un mot, on peut voir l’ensemble des URL qui y sont associées classées par pertinence.

Multiplication d’une matrice et d’un vecteur

Cette exemple est légèrement plus complexe car il faut savoir comment multiplier une matrice à un vecteur.

Si on considère une matrice de taille 3×3:

Et un vecteur de taille 3:

L’opération de multiplication d’une matrice et d’un vecteur consiste à additionner chaque ligne de la matrice aux valeurs du vecteur ce qui donne trois opérations. Dans notre cas:

  • 1ère opération: 2*6 + 8*3 + 0*5 = 36
  • 2è operation: 0*6 + 4*3 + 1*5 = 17
  • 3e opération: 5*6 + 0*3 + 3*5 = 45

Le résultat de l’opération est le vecteur:

Pour effectuer ce calcul avec un job MapReduce, on va considérer un triplet (i, j, aij) avec i le numéro de ligne, j le numéro de colonne et aij la valeur. Si la valeur dans la matrice est 0, elle ne sera pas représentée.

Dans cet exemple, on effectue le calcul pour une matrice de petite taille toutefois, pour justifier l’utilisation d’un job MapReduce et pour que ce calcul soit pertinent, il faudrait une opération dont la matrice est de taille très grande qui nécessiterait plusieurs minutes de calcul.

Dans le cas de notre exemple, les opérations du job MapReduce sont:

  • Map: dans un premier temps, on considère que les valeurs du vecteur sont représentables sous la forme (i, vi) et que les valeurs de la matrice sont aij (pour la valeur située à la i-ème ligne et j-ème colonne). L’opération Map consiste à extraire une paire (i, aij * vi) qui consiste à effectuer la multiplication de la valeur aij (située à la ligne i et à la colonne j de la matrice) avec la valeur du vecteur vi (située à la ligne i du vecteur).

    Sachant que le résultat est un vecteur, on peut représenter le résultat de l’opération sous la forme de la paire indiquée précédemment avec i étant le numéro de ligne comme clé de la paire. La valeur étant le résultat de la multiplication aij * vi.

    Il y a donc, autant de tâche Map que de multiplication à effectuer. Dans notre cas, les paires obtenues sont:
    Pour la 1ère ligne (0 est l’indice de la 1ère ligne):

    • 1ère opération Map: (0, 2 * 6) soit (0,12)
    • 2e opération Map: (0, 8 * 3) soit (0, 24)
    • Il n’y a pas de 3è opération pour la 1ère ligne car la valeur du vecteur est 0.

    Pour la 2e ligne (1 est l’indice de la 2e ligne):

    • 3e opération Map: (1, 4 * 3) soit (1, 12). Comme précédemment à cause du 0, on passe directement au calcul de la 2è valeur de la ligne.
    • 4e opération Map: (1, 1 * 5) soit (1, 5)

    Pour la 3e ligne (2 est l’indice de la 2e ligne):

    • 5e opération Map: (2, 5 * 6) soit (2, 30)
    • 6e opération Map: (2, 3 * 5) soit (2, 15)

    A l’issue de toutes les opérations Map, on obtient la liste de paires: (0,12), (0, 24), (1, 12), (1, 5), (2, 30) et (2, 15).
    Cette liste est indiquée de façon ordonnée toutefois dans la réalité elle n’est pas ordonnée car les opérations Map se font de façon parallèle et les résultats ne sont pas forcément ordonnancés de cette façon.

  • Shuffle et Sort: cette étape va ordonner les paires suivant leur clé et répartir équitablement les paires sur les nœuds. On pourrait avoir une répartition de cette façon:
    • Node 1: (0,12), (0, 24), (1, 12), (1, 5)
    • Node 2: (2, 30) et (2, 15)
  • Reduce: cette étape va permettre d’agréger toutes les valeurs suivant leur clé. On aura donc la liste de valeurs:
    • (0, [12, 24])
    • (1, [12, 5])
    • (2, [30, 15])

    En additionnant les valeurs agrégées, on obtient la liste: (0, 36), (1, 17) et (2, 45). Sachant que les clés correspondent à l’indice de ligne du vecteur, on a bien le résultat de la multiplication:

En conclusion

Le but de cet article était d’expliquer l’architecture d’Hadoop et le fonctionnement d’un job MapReduce. Il n’est pas rentré dans le détail de l’implémentation d’un mapper ou d’un reducer car ces composants feront l’objet d’un article futur sur le sujet. Cet article sert de base théorique de façon à aborder un job MapReduce de façon plus approfondie par la suite.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Commandes shell courantes pour HDFS

Quelques commandes courantes pour HDFS

Pour ces commandes, il existe 2 syntaxes possibles:

  • Avec hadoop: avec une syntaxe du type hadoop fs <commande>,
  • Avec hdfs: la syntaxe est hdfs dfs <commande>.

Cette commande sont proche de celles utilisées par le shell linux comme ls, mkdir, rm, cat, etc…

Pour lister le contenu d’un répertoire

hdfs dfs -ls <chemin du répertoire>

Par exemple:

hdfs dfs -ls /
hdfs dfs -ls /user  # pour voir le contenu du répertoire "user"
Found 2 items
-rw-r--r--   1 hduser supergroup    3324334 2017-09-16 12:00 /user/135-0.txt
-rw-r--r--   1 hduser supergroup    3359550 2017-09-16 12:01 /user/2600-0.txt

On peut utiliser aussi:

hadoop fs -ls /user

Pour afficher le contenu d’un fichier


hdfs dfs -cat <chemin du fichier>

Par exemple:

hdfs dfs -cat /user/135-0.txt

On peut utiliser:

hadoop fs -cat /user/135-0.txt

Pour créer un répertoire

hdfs dfs -mkdir <chemin du nouveau répertoire>

Par exemple:

hdfs dfs -mkdir /user/output

Pour copier un fichier sur HDFS

On peut utiliser:

hdfs dfs -put <chemin du fichier source> <chemin du fichier destination sur HDFS>

La commande suivante est réservé seulement au fichier locaux:

hdfs dfs -copyFromLocal  <chemin du fichier source> <chemin du fichier destination sur HDFS>

Par exemple:

hdfs dfs -put TextFile.txt /user

ou

hdfs dfs -copyFromLocal TextFile.txt /user

Les syntaxes équivalentes avec hadoop sont possibles:

hadoop fs -put <chemin du fichier source> <chemin du fichier destination sur HDFS>
hadoop fs -copyFromLocal <chemin du fichier source> <chemin du fichier destination sur HDFS>

Pour effectuer un copie de fichier

hdfs dfs -cp <chemin du fichier source sur HDFS> <chemin du fichier destination sur HDFS>

Par exemple:

hdfs dfs -cp /user/TextFile.txt /user/output
hdfs dfs -cp /user/TextFile.txt /user/TestFile2.txt

Avec hadoop:

hadoop fs -cp /user/TextFile.txt /user/output
hadoop fs -cp /user/TextFile.txt /user/TestFile2.txt

Pour récupérer un fichier sur HDFS

hdfs dfs -get <chemin du fichier sur HDFS> <chemin du fichier en local>

Par exemple:

hdfs dfs -get /user/TextFile2.txt 
hdfs dfs -get /user/TextFile2.txt LocalTextFile2.txt

Cette syntaxe est réservée aux fichiers locaux:

hdfs dfs -copyToLocal /user/TextFile2.txt

ou

hadoop fs -get /user/TextFile2.txt
hadoop fs -copyToLocal /user/TextFile2.txt

Les mêmes syntaxes existent pour effectuer des déplacements:

  • hdfs dfs -moveToLocal pour déplacer de HDFS vers le volume local
  • hdfs dfs -moveFromLocal pour déplacer du volume local vers HDFS
  • hdfs dfs -mv pour effectuer des déplacements dans HDFS

Pour supprimer un fichier

hdfs dfs -rm <chemin du fichier sur HDFS>

Par exemple:

hdfs dfs -rm /user/TextFile2.txt
Deleted /user/TextFile2.txt

ou

hadoop fs -rm /user/TextFile2.txt

Pour supprimer un répertoire

Si le répertoire est vide, on peut utiliser comme sur le shell rmdir:

hdfs dfs -rmdir <chemin du répertoire vide>

Par exemple:

hdfs dfs -rmdir /user/output2

Si le répertoire contient des fichiers:

hdfs dfs -rm -r <chemin du répertoire>

Par exemple:

hdfs dfs -rm -r /user/output

Avec hadoop:

hadoop fs -rmdir /user/output2
hadoop fs -rm -r /user/output
Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Installation d’Hadoop sur Debian

Cet article explique l’installation de Hadoop sur Debian GNU/linux 9. La version d’Hadoop utilisée est celle téléchargeable directement du site d’Apache.
L’intérêt d’utiliser Debian est que beaucoup d’outils sont disponibles sur cette distribution et qu’elle est gratuite.
Dans cet article, on détaille l’installation d’Hadoop et de YARN ainsi que la configuration d’un cluster en single-node.

Une version de Debian installée sur une machine virtuelle est une configuration très flexible pour essayer Hadoop sans polluer son système d’exploitation hôte. L’installation de Debian sur VirtualBox est facile à réaliser (cf. Installation de Debian sur une machine virtuelle VirtualBox). Toutefois cette étape n’est pas obligatoire car il est possible d’installer Hadoop directement sur plusieurs types de système d’exploitation.

1. Installation JDK

Avant d’installer Hadoop, il faut installer Java si ce n’est pas déjà fait.
On peut télécharger la JDK sur le site d’Oracle: http://www.oracle.com/technetwork/java/javase/downloads/index.html.

La version utilisée était la jdk1.8.0_144 pour linux en 64 bits.

On peut installer la JDK sur le disque en exécutant:

tar zxvf jdk-8u144-linux-x64.tar.gz

Cette commande permet d’extraire les fichiers contenus dans l’archive en listant tous les fichiers extraits.

En tant qu’utilisateur root, placer le répertoire résultant dans /usr/java. Pour passer en tant qu’utilisateur root, il faut taper:

su

Puis pour copier le répertoire:

mkdir /usr/java
mv <nom du répertoire> /usr/java

2. Télécharger Hadoop sur le site d’Apache

On peut télécharger Hadoop sur le site d’Apache: http://hadoop.apache.org/.

La version à installer est la dernière version stable sur http://hadoop.apache.org/releases.html.
Dans notre cas la version était la 2.8.1.

Il faut extraire l’archive et la placer dans /etc/hadoop:

tar zxvf hadoop-2.8.1.tar.gz

Et:

mkdir /etc/hadoop
mv <nom du répertoire> /usr/hadoop

3. Préparation de l’installation d’Hadoop

Création d’un groupe et d’un utilisateur Hadoop

Cette étape n’est pas indispensable mais permet de séparer les permissions. Cet utilisateur sera spécifique à Hadoop.
Pour créer le groupe “hadoop” et l’utilisateur “hduser”, il faut exécuter en tant qu’utilisateur root (taper su pour switcher vers l’utilisateur root):

addgroup hadoop
adduser --ingroup hadoop hduser

On peut ensuite se connecter avec cet utilisateur en écrivant:

su hduser

Installation SSH

Il faut installer SSH en tapant en tant qu’utilisateur root:

apt-get install ssh

Il faut aussi installer rsync qui permet des synchronisations de fichiers à distance en utilisant SSH. L’installation se fait en écrivant:

apt-get install rsync

Configuration SSH

L’utilisation d’Hadoop se fait par l’intermédiaire d’une connexion SSH même si Hadoop est exécuté localement. Il faut autoriser l’utilisateur “hduser” à se connecter en SSH sur la machine en créant une clé et en autorisant cette clé sur la machine.

La création de la clé se fait en exécutant les commandes suivantes. Il faut être connecté en tant qu’utilisateur “hduser”:

ssh-keygen -t rsa -P ""

L’option -t indique qu’on souhaite créer une clé de type rsa. L’option -P sert à indiquer une passphrase qui dans ce cas est vide.

La clé est rangée dans le répertoire /home/hduser/.ssh/id_rsa.pub.

Il faut ajouter cette clé aux clef autorisées pour les connexions SSH en exécutant:

cat /home/hduser/.ssh/id_rsa.pub >> /home/hduser/authorized_keys

Cette commande va rajouter le contenu de id_rsa.pub dans le fichier authorized_keys en le créant s’il n’existe pas.

On peut ensuite tester l’accès en SSH en écrivant:

ssh localhost

Il faut ensuite accepter la connexion en écrivant “yes”. L’accès doit se faire sans passphrase puisqu’on a indiqué une passphrase vide plus haut.

On peut sortir de la connexion SSH en écrivant:

exit

4. Configuration d’Hadoop en single-node cluster

L’installation peut se faire en suivant les instructions sur: Hadoop: Setting up a Single Node Cluster.

Cette installation permet de configurer Hadoop pour que le cluster ne soit former que d’un seul nœud.

Configuration d’Hadoop

Dans un premier temps, il faut configurer le chemin de la JDK dans le fichier de configuration d’Hadoop hadoop-env.sh. Ce fichier se trouve dans /usr/hadoop/hadoop-2.8.1/etc/hadoop/hadoop-env.sh. On peut l’éditer en tant qu’utilisateur root et en remplaçant:

export JAVA_HOME=${JAVA_HOME}

par:

export JAVA_HOME=/usr/java/jdk1.8.0_144/

On peut ensuite exécuter Hadoop en tapant:

/usr/hadoop/hadoop-2.8.1/bin/hadoop

L’exécution devrait afficher l’aide concernant Hadoop:

Usage: hadoop [--config confdir] [COMMAND | CLASSNAME]
  CLASSNAME            run the class named CLASSNAME
 or
  where COMMAND is one of:
  fs                   run a generic filesystem user client
  version              print the version
  jar <jar>            run a jar file
                       note: please use "yarn jar" to launch
                             YARN applications, not this command.
  checknative [-a|-h]  check native hadoop and compression libraries availability
  distcp <srcurl> <desturl> copy file or directories recursively
  archive -archiveName NAME -p <parent path> <src>* <dest> create a hadoop archive
  classpath            prints the class path needed to get the
                       Hadoop jar and the required libraries
  credential           interact with credential providers
  daemonlog            get/set the log level for each daemon
  trace                view and modify Hadoop tracing settings

Most commands print help when invoked w/o parameters.

Configuration du cluster en mode pseudo-distribué

Cette configuration correspond à la partie “Pseudo-Distributed operation”. Elle permet de configurer le cluster en mode pseudo-distribué (puisqu’il n’y a qu’une seule machine).

Il faut éditer le fichier core-site.xml dans le répertoire: /usr/hadoop/hadoop-2.8.1/etc/hadoop/core-site.xml en ajoutant:

<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://localhost:9000</value>
    </property>
</configuration>

Cette configuration permet d’indiquer que le nom du système de fichier est fs.defaultFS et que les répertoires et fichiers dans HDFS sont préfixés avec: hdfs://localhost:9000.

On édite ensuite le fichier qui se trouve dans: /usr/hadoop/hadoop-2.8.1/etc/hadoop/hdfs-site.xml et on ajoute les éléments suivants:

<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
</configuration>

Cette configuration permet d’indiquer le nombre de réplication d’un bloc qui sera de 1 car il n’y a qu’un seul nœud dans le cluster.

Changement des droits sur le répertoire d’Hadoop

Pour permettre l’exécution d’Hadoop par l’utilisateur “hduser”, il faut changer les droits sur le répertoire d’Hadoop en tapant:

chown -R hduser:hadoop /usr/hadoop/hadoop-2.8.1

Execution de jobs localement

En se connectant en tant qu’utilisateur “hduser” (en tapant su hduser), on peut formater le système de fichiers de HDFS en tapant:

/usr/hadoop/hadoop-2.8.1/bin/hdfs namenode -format

On peut créer le répertoire qui va contenir les logs lors de l’exécution en écrivant:

mkdir /usr/hadoop/hadoop-2.8.1/logs

On peut paramétrer les variables d’environnement pour ajouter aux chemins le répertoire d’Hadoop. Il faut éditer le fichier /home/hduser/.bashrc en ajoutant:

export JAVA_HOME=/usr/java/jdk1.8.0_144/
export HADOOP_HOME=/usr/hadoop/hadoop-2.8.1/
export PATH=$PATH:$HADOOP_HOME/bin
export PATH=$PATH:$HADOOP_HOME/sbin

Pour prendre en compte les modifications des variables d’environnement, on peut redémarrer sa session de terminal.

On peut démarrer ensuite le daemon du NameNode et du datanode en exécutant:

start-dfs.sh

Sachant que le répertoire contenant ce fichier a été rajouté dans la variable d’environnement PATH, on peut exécuter cette action de n’importe où. Toutefois si ce n’est pas le cas, ce fichier se trouve dans /usr/hadoop/hadoop-2.8.1/sbin/start-dfs.sh.

Les logs correspondant à cette action sont dans:

/usr/hadoop/hadoop-2.8.1/logs

L’exécution devrait indiquer:

Starting namenodes on [localhost]
localhost: starting namenode, logging to /usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-namenode-debianvm.out
localhost: starting datanode, logging to /usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-datanode-debianvm.out
Starting secondary namenodes [0.0.0.0]
0.0.0.0: starting secondarynamenode, logging to /usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-secondarynamenode-debianvm.out

On peut se connecter à l’interface web du NameNode à l’adresse:

http://localhost:50070/

On devrait voir une interface du type:

Interface cluster Hadoop

Erreur “Connection refused”

Une erreur “Connection refused” peut se produire quand on essaie de démarrer Hadoop avec start-dfs.sh. Cette erreur est assez déroutante puisqu’elle peut se produire sans raison particulière alors que le cluster a déjà fonctionné.

On peut s’apercevoir de cette erreur après exécution de start-dfs.sh et quand on essaie d’accéder à l’interface d’Hadoop en se connectant à l’adresse http://localhost:50070.

Pour savoir s’il s’agit bien de la même erreur, il suffit d’aller dans le répertoire des fichiers de logs:

vi /usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-datanode-<nom de la machine>.log

Hadoop refuse de démarrer et loggue une erreur du type:

2017-09-16 09:41:21,836 WARN org.apache.hadoop.ipc.Client: Failed to connect to server: localhost/127.0.0.1:9000: retries get failed due to exceeded maximum allowed retries number: 10
java.net.ConnectException: Connection refused
	at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method)
	at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:717)
	at org.apache.hadoop.net.SocketIOWithTimeout.connect(SocketIOWithTimeout.java:206)
	at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:531)
	at org.apache.hadoop.net.NetUtils.connect(NetUtils.java:495)
	at org.apache.hadoop.ipc.Client$Connection.setupConnection(Client.java:681)
	at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:777)
	at org.apache.hadoop.ipc.Client$Connection.access$3500(Client.java:409)
	at org.apache.hadoop.ipc.Client.getConnection(Client.java:1542)
	at org.apache.hadoop.ipc.Client.call(Client.java:1373)
	at org.apache.hadoop.ipc.Client.call(Client.java:1337)
	at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:227)
	at org.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:116)
	at com.sun.proxy.$Proxy15.versionRequest(Unknown Source)
	at org.apache.hadoop.hdfs.protocolPB.DatanodeProtocolClientSideTranslatorPB.versionRequest(DatanodeProtocolClientSideTranslatorPB.java:274)
	at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.retrieveNamespaceInfo(BPServiceActor.java:215)
	at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.connectToNNAndHandshake(BPServiceActor.java:261)
	at org.apache.hadoop.hdfs.server.datanode.BPServiceActor.run(BPServiceActor.java:746)
	at java.lang.Thread.run(Thread.java:748)

Toutes les tentatives futures pour se connecter à l’adresse localhost/127.0.0.1:9000 ne fonctionnent pas:

017-09-16 09:43:22,011 WARN org.apache.hadoop.hdfs.server.datanode.DataNode: Problem connecting to server: localhost/127.0.0.1:9000
2017-09-16 09:43:28,013 INFO org.apache.hadoop.ipc.Client: Retrying connect to server: localhost/127.0.0.1:9000. Already tried 0 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS)
2017-09-16 09:43:29,013 INFO org.apache.hadoop.ipc.Client: Retrying connect to server: localhost/127.0.0.1:9000. Already tried 1 time(s); retry policy is RetryUpToMaximumCountWithFixedSleep(maxRetries=10, sleepTime=1000 MILLISECONDS)
etc...

L’erreur semble provenir du fait qu’Hadoop est sensible aux redirections DNS effectuées par Linux entre l’adresse localhost dirigée vers 127.0.0.1 et le nom réseau de la machine dirigée vers 127.0.1.1.

Tout d’abord avant de commencer à changer la configuration, il faut stopper l’exécution d’Hadoop et de YARN si ce n’est pas déjà fait (en tant qu’utilisateur “hduser”) en exécutant:

/usr/hadoop/hadoop-2.8.1/sbin/stop-yarn.sh
/usr/hadoop/hadoop-2.8.1/sbin/stop-dfs.sh

Ensuite, il faut connaître le nom réseau utilisé par Hadoop en exécutant:

hostname --fqdn

Le résultat est le nom réseau utilisé.

Pour corriger le problème, il faut éditer le fichier /etc/hosts en tant qu’utilisateur root:

su
vi /etc/hosts

Par défaut le fichier se présente de cette façon:

127.0.0.1	localhost
127.0.1.1	<nom réseau de la machine>

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Il faut paramétrer la même adresse pour le nom réseau de la machine et commenter les lignes correspondant à IPv6:

127.0.0.1	localhost
127.0.0.1	<nom réseau de la machine>

# The following lines are desirable for IPv6 capable hosts
#::1     localhost ip6-localhost ip6-loopback
#ff02::1 ip6-allnodes
#ff02::2 ip6-allrouters

Il faut ensuite affiner la configuration pour que certains fichiers temporaires soient créés dans le répertoire de l’utilisateur “hduser”. Il faut éditer le fichier core-site.xml et ajouter quelques éléments de configuration:

vi /usr/hadoop/hadoop-2.8.1/etc/hadoop/core-site.xml

Après avoir ajouté les éléments, le fichier doit se présenter de cette façon:

<configuration>
 <property>
   <name>fs.defaultFS</name>
   <value>hdfs://localhost:9000</value>
   <description>The name of the default file system.</description>
 </property>
 <property>
   <name>hadoop.tmp.dir</name>
   <value>/home/hduser/hadoop_data/hd-data/tmp</value>
 </property>
 <property>
   <name>fs.checkpoint.dir</name>
   <value>/home/hduser/hadoop_data/hd-data/snn</value>
 </property>
 <property>
   <name>dfs.data.dir</name>
   <value>/home/hduser/hadoop_data/hd-data/dn</value>
 </property>;
</configuration>

Les parties en gras sont les parties nouvelles par rapport à la configuration précédente.

Il faut ensuite créer le répertoire hadoop_data dans le répertoire de l’utilisateur “hduser” (cette étape doit être effectuée en tant qu’utilisateur “hduser” pour qu’Hadoop puisse écrire dans le répertoire):

mkdir /home/hduser/hadoop_data

Il faut ensuite supprimer les fichiers temporaires d’Hadoop (en tant qu’utilisateur root):

rm -r /tmp/hadoop*

Enfin, il faut reformater HDFS en exécutant (en tant qu’utilisateur “hduser”):

/usr/hadoop/hadoop-2.8.1/hdfs namenode -format

On peut ensuite essayer de démarrer Hadoop avec:

/usr/hadoop/hadoop-2.8.1/sbin/start-dfs.sh

Le fichier log du NameNode devrait ne plus comporter l’erreur “Connection refused”. On peut consulter ce fichier de log et les autres fichiers pour le vérifier. Ces fichiers se trouvent dans:

/usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-datanode-<nom de la machine>.log
/usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-namenode-<nom de la machine>.log
/usr/hadoop/hadoop-2.8.1/logs/hadoop-hduser-secondarynamenode-<nom de la machine>.log

On peut tenter de se connecter à l’interface web d’Hadoop sur:

http://localhost:50070

5. YARN

Configuration de YARN

Cette étape permet de configurer le gestionnaire de cluster YARN (pour Yet Another Resource Negotiator) en nœud simple (ie. single-node). Il faut éditer le fichier /usr/hadoop/hadoop-2.8.1/etc/hadoop/mapred-site.xml (ou le créer s’il n’existe pas en copiant mapred-site.xml.template) et ajouter:

<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
</configuration>

Cette configuration permet d’indiquer d’utiliser YARN en tant qu’implémentation de MapReduce.

Il faut ensuite éditer le fichier /usr/hadoop/hadoop-2.8.1/etc/hadoop/yarn-site.xml et ajouter:

<configuration>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
</configuration>

Exécution de YARN

Après la configuration, on peut démarrer le daemon ResourceManager et NodeManager en tapant:

start-yarn.sh

Cette commande est dans un répertoire rajouté dans la variable d’environnement PATH donc elle doit être accessible de n’importe où. Ce fichier se trouve dans /usr/hadoop/hadoop-2.8.1/sbin/start-yarn.sh.

L’interface web du ResourceManager est accessible à l’adresse:

http://localhost:8088/

L’interface est du type:

Interface YARN

6. Execution d’un job MapReduce

Le job le plus couramment exécuté pour illustrer l’exécution de jobs MapReduce est le Wordcount (i.e. comptage de mots). Il faut utiliser en entrée un texte et en sortie du job, on obtient une liste de mots avec le nombre d’occurences pour chaque mot.

Comme texte d’entrée, on peut utiliser le fichier correspondant au livre Guerre et Paix:
http://www.gutenberg.org/files/2600/2600-0.txt

Il faut copier ce fichier dans HDFS en écrivant:

hdfs dfs -mkdir /user
hdfs dfs -mkdir /user/hduser
hdfs dfs -mkdir /user/hduser/input
hdfs dfs -put 2600-0.txt /user/hduser/input

A ce moment on peut voir le fichier dans HDFS si on va dans l’interface web de Hadoop à l’adresse http://localhost:50070 dans l’onglet Utilities ➔ Browse the file system:

HDFS

On peut lancer le job en écrivant:

hadoop jar /usr/hadoop/hadoop-2.8.1/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.8.1.jar wordcount /user/hduser/input/2600-0.txt output

Cette ligne permet de lancer le job “wordcount” qui est implémenté dans le fichier JAR hadoop-examples-2.8.1.jar avec en entrée le fichier 2600-0.txt qui a été copié dans HDFS. Le résultat sera écrit dans le répertoire output dans HDFS.

L’exécution donnera en sortie:

17/09/09 12:16:44 INFO client.RMProxy: Connecting to ResourceManager at /0.0.0.0:8032
17/09/09 12:16:45 INFO input.FileInputFormat: Total input files to process : 1
17/09/09 12:16:46 INFO mapreduce.JobSubmitter: number of splits:1
17/09/09 12:16:47 INFO mapreduce.JobSubmitter: Submitting tokens for job: job_1504949218149_0001
17/09/09 12:16:48 INFO impl.YarnClientImpl: Submitted application application_1504949218149_0001
17/09/09 12:16:48 INFO mapreduce.Job: The url to track the job: http://debianvm:8088/proxy/application_1504949218149_0001/
17/09/09 12:16:48 INFO mapreduce.Job: Running job: job_1504949218149_0001
17/09/09 12:17:09 INFO mapreduce.Job: Job job_1504949218149_0001 running in uber mode : false
17/09/09 12:17:09 INFO mapreduce.Job:  map 0% reduce 0%
17/09/09 12:17:39 INFO mapreduce.Job:  map 100% reduce 0%
17/09/09 12:17:53 INFO mapreduce.Job:  map 100% reduce 100%
17/09/09 12:17:57 INFO mapreduce.Job: Job job_1504949218149_0001 completed successfully
17/09/09 12:17:57 INFO mapreduce.Job: Counters: 49
	File System Counters
		FILE: Number of bytes read=649694
		FILE: Number of bytes written=1571945
		FILE: Number of read operations=0
		FILE: Number of large read operations=0
		FILE: Number of write operations=0
		HDFS: Number of bytes read=3359665
		HDFS: Number of bytes written=487290
		HDFS: Number of read operations=6
		HDFS: Number of large read operations=0
		HDFS: Number of write operations=2
	Job Counters 
		Launched map tasks=1
		Launched reduce tasks=1
		Data-local map tasks=1
		Total time spent by all maps in occupied slots (ms)=27460
		Total time spent by all reduces in occupied slots (ms)=10545
		Total time spent by all map tasks (ms)=27460
		Total time spent by all reduce tasks (ms)=10545
		Total vcore-milliseconds taken by all map tasks=27460
		Total vcore-milliseconds taken by all reduce tasks=10545
		Total megabyte-milliseconds taken by all map tasks=28119040
		Total megabyte-milliseconds taken by all reduce tasks=10798080
	Map-Reduce Framework
		Map input records=66055
		Map output records=566308
		Map output bytes=5541955
		Map output materialized bytes=649694
		Input split bytes=115
		Combine input records=566308
		Combine output records=41991
		Reduce input groups=41991
		Reduce shuffle bytes=649694
		Reduce input records=41991
		Reduce output records=41991
		Spilled Records=83982
		Shuffled Maps =1
		Failed Shuffles=0
		Merged Map outputs=1
		GC time elapsed (ms)=210
		CPU time spent (ms)=6020
		Physical memory (bytes) snapshot=412577792
		Virtual memory (bytes) snapshot=3924443136
		Total committed heap usage (bytes)=284688384
	Shuffle Errors
		BAD_ID=0
		CONNECTION=0
		IO_ERROR=0
		WRONG_LENGTH=0
		WRONG_MAP=0
		WRONG_REDUCE=0
	File Input Format Counters 
		Bytes Read=3359550
	File Output Format Counters 
		Bytes Written=487290

On peut voir le fichier contenant les résultats en écrivant:

hdfs dfs -ls /user/hduser/output

Les fichiers sur HDFS sont:

Found 2 items
-rw-r--r--   1 hduser supergroup          0 2017-09-09 12:17 /user/hduser/output/_SUCCESS
-rw-r--r--   1 hduser supergroup     487290 2017-09-09 12:17 /user/hduser/output/part-r-00000

On peut récupérer le fichier contenant les résultats en écrivant:

hdfs dfs -get /user/hduser/output/part-r-00000

On peut lire maintenant le fichier. Le contenu est une liste de mots avec le nombre d’occurences pour chaque mot.

Pour supprimer les fichiers se trouvant sur HDFS, il faut exécuter:

hdfs dfs -rm -R /user/hduser/output
hdfs dfs -rm -R /user/hduser/input

7. Stopper l’exécution de YARN et Hadoop

Il faut taper les commandes suivantes:

stop-yarn.sh

Si l’arrêt se passe bien, l’exécution est du type:

stopping yarn daemons
stopping resourcemanager
localhost: stopping nodemanager
localhost: nodemanager did not stop gracefully after 5 seconds: killing with kill -9
no proxyserver to stop
stop-dfs.sh

Le résultat peut être du type:

Stopping namenodes on [localhost]
localhost: stopping namenode
localhost: stopping datanode
Stopping secondary namenodes [0.0.0.0]
0.0.0.0: stopping secondarynamenode
Références
Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Installation de Debian sur une machine virtuelle VirtualBox

Le but de cet article est d’expliquer l’installation de Debian GNU/Linux 9 sur une machine virtuelle avec VirtualBox. Utiliser une machine virtuelle peut avoir de nombreux avantages quand on cherche à tester de nouvelles technologies:

  • On peut exporter la machine virtuelle si on souhaite partager une configuration.
  • En cas de mauvaise installation, on peut relancer une nouvelle instance d’une machine virtuelle et recommencer l’installation.
  • On ne pollue pas son système d’exploitation principal avec des installations qui ne vont pas forcément servir par la suite. On peut être libre de tester de nouveaux outils sans craindre qu’ils soient compliqués à désinstaller.

GNU Debian/Linux et VirtualBox sont gratuits et peuvent être utilisés sans problématique de licence. Cette configuration permet de tester de nombreux outils qui sont installables dans un environnement Linux comme Docker, .NET Core, Teamcity, Hadoop, Spark, MongoDB, etc…

Enfin, VirtualBox peut être installé sur tous les environnements ce qui donne davantage de flexibilité.

1. Installation de VirtualBox

La prmeière étape consiste à installer Oracle VM VirtualBox. Cet outil peut être téléchargé sur:
https://www.virtualbox.org/
Quelque soit la plateforme, cette étape ne présente pas de difficulté particulière.

2. Créer une nouvelle VM

Pour créer une machine virtuelle (i.e. VM) dans VirtualBox, il faut:

  • Cliquer sur “Nouvelle” puis choisir le type “Linux” et version “Debian (64-bit)”
  • Taille de la mémoire: 1024 Mo au minimum (ce paramètre peut être ajusté par la suite) toutefois on peut paramétrer une valeur plus confortable (par exemple 4096 Mo).
  • Pour la taille du disque, il faut prévoir une assez grande taille dès le début pour éviter d’être trop à l’étroit. Ce paramètre ne pourra pas être ajusté par l’interface graphique pas la suite. Il est possible d’augmenter la taille du disque après en utilisant l’instruction:
    VBoxManage modifyhd --resizebyte <taille voulue en MO> <chemin du fichier VDI>
    

    Toutefois cette ligne ne fonctionne pas toujours.

Création de la machine virtuelle
Ajustement du paramètre de mémoire

Pour le disque dur donc choisir: “Créer un disque dur virtuel maintenant”.
Puis “VDI (Image Disque VirtualBox)”.
Et “Dynamiquement alloué”.
Ensuite ajuster la taille à 30 GO pour ne pas manquer d’espace disque (cette taille n’est pas allouée tout de suite, elle ne sera utilisée que si la VM consomme réellement l’espace).

Création du disque dur

3. Ajuster quelques paramètres sur la VM

On peut affiner quelques paramètres dans la VM en effectuant un clique droit sur la VM puis en cliquant sur “Configuration”.

Par exemple, on peut affiner le réglage de la mémoire vidéo dans l’onglet “Affichage” puis “Ecran” (ce paramétrage peut être ajusté après la création de la VM).

Ajustement de la mémoire vidéo

On peut aussi ajuster le nombre de coeurs du processeur dans l’onglet “Système” puis “Processeur”:

  • Sélectionnez “4”.
  • Valider en cliquant sur “OK”.
Réglage du nombre de processeur

4. Télécharger Debian 9

Il faut ensuite télécharger l’image ISO d’installation de GNU Debian/Linux sur https://www.debian.org/:

  1. Aller dans “Images ISO cd CD/USB”.
  2. Choisir la méthode de téléchargement, par exemple “Télécharger les images des CD ou DVD par HTTP ou FTP”
  3. Choisir la distribution “stable”, actuellement il s’agit de “Stretch” (Debian 9).
  4. Cliquer sur “images officielle des CD ou DVD de la distribution ‘stable’
  5. Choisir l’architecture, pour une machine 64 bits, ça sera “amd64”.
    L’image de base suffit, par exemple: “debian-9.x.x-amd64-netinst”

5. Installer Debian

Pour commencer l’installation de Debian sur la machine virtuelle, il faut effectuer les étapes suivantes:

  1. Effectuer un clique droit sur la VM puis cliquer sur “Configuration”.
  2. Aller dans l’onglet “Stockage” puis cliquer sur “Controleur IDE” puis “Vide”:
    Montage de l’image de Debian
  3. Cocher ensuite “Live CD/DVD” puis cliquer sur l’icone du disque et cliquer sur “Choisissez un fichier de disque optique virtuel…”:

    Sélectionner l’image de Debian
  4. Sélectionnez l’image ISO téléchargée puis valider par “OK”.
  5. Cliquer ensuite sur “OK” pour valider la configuration de la VM.
  6. Cliquer ensuite sur “Démarrer” pour lancer la VM:
    Démarrer la VM

A ce stade l’installateur de Debian est lancé.
Choisir “Graphical install”:

Installateur de Debian

L’installation est ensuite classique:

  1. Choisir la langue, par exemple “English”.
  2. “Select a location”: la position dans l’écran “Location”: cliquer sur “other” ➜ “Europe” ➜ “France”.
  3. “Configure locales”: par exemple “United Kingdom”.
  4. “Configure the keyboard”: sélectionnez “French”.
  5. “Configure the network”: indiquer un nom réseau, par exemple “debianvm”.
    indiquer ensuite un nom de domaine: ce paramètre peut rester vide:

    Ecran “Configure the network”
  6. “Set up users and passwords”: choisir un mot de passe pour l’utilisateur root.
    choisir ensuite le nom d’un utilisateur, son nom de compte puis son mot de passe.

    Ecran “Set up users and passwords”
  7. “Partition disks”: choisir “Guided partitioning” ➜ “Guided – use entire disk” ➜ Sélectionnez ensuite le disque présent.
    Ecran “Partition disks (1/6)”
    Ecran “Partition disks (2/6)”
    Ecran “Partition disks (3/6)”
  8. Sélectionez ensuite “All files in one partition (recommended for new users)”.
    Puis “Finish partitioning and write changes to disk”.

    Ecran “Partition disks (4/6)”
    Ecran “Partition disks (5/6)”
  9. A la question “Write the changes to disks”, sélectionnez “Yes”:
    Ecran “Partition disks (6/6)”
  10. “Configure the package manager”: sélectionnez “No” puis “France” et “ftp.fr.debian.org”.
    Ecran “Configure the package Manager (1/4)”
    Ecran “Configure the package Manager (2/4)”
    Ecran “Configure the package Manager (3/4)”
  11. Indiquez éventuellement des informations de proxy:
    Ecran “Configure the package Manager (4/4)”
  12. “Configuring popularity-contest”: sélectionnez la valeur de votre choix.
  13. “Software selection”:
    Cochez “Debian desktop environment” et “standard system utilities”
    Décochez “print server”

    GNOME sélectionné par défaut

    Avec ce paramétrage, GNOME sera sélectionné par défaut. Bien que GNOME soit un environnement de bureaux facile à utiliser, il fait partie des environnements les plus lourds. Pour économiser les ressources on peut, par exemple, sélectionner Xfce qui est plus léger mais moins riche en utilitaires.

    Ecran “Software selection”
  14. “Install the GRUB boot loader on a hard disk”:
    Cliquer sur “Yes” pour installer GRUB:

    Ecran “Install the GRUB boot loader on a hard disk (1/2)”
  15. Sélectionnez le disque c’est-à-dire “/dev/sda”:
    Ecran “Install the GRUB boot loader on a hard disk (2/2)”

L’installation se termine par:

Fin de l’installation

6. Démarrage de Debian

A la fin de l’installation, Debian redémarre automatiquement.
Si ce n’est pas le cas, il faut démarrer manuellement la VM mais il faut enlever le disque virtuel d’installation en allant dans la configuration de la VM:

  1. Faire un clique droit sur la VM puis “Configuration”.
  2. Allez dans l’onglet “Stockage” puis sélectionnez le disque ISO “debian-9.x.x-amd64-netinst.iso”.
    Décochez “Live CV/DVD”
    Cliquez sur l’icone du disque puis clique sur “Retirer le disque du lecteur virtuel”
    Valider en cliquant sur “OK”.

    Retirer le disque Debian
  3. Démarrer ensuite la VM en cliquant sur Démarrer.

On arrive ensuite sur l’invite GRUB de sélection du système à démarrer. La sélection est automatique, il suffit d’attendre quelques secondes:

Invite GRUB

Entrez le mot de passe si nécessaire en cliquant sur le nom de l’utilisateur:

Mot de passe pour l’ouverture de session

On arrive ensuite sur l’interface de Debian:

Interface de GNOME

7. Installation des “VirtualBox Guest Additions”

Les VirtualBox Guest Additions sont des composants qui permettent de personnaliser des éléments de configuration dans Debian en accord avec la VirtualBox. Par exemple, pour configurer correctement la résolution de la VM.

Il faut télécharger l’image ISO des VirtualBox Guest Additions sur:
http://download.virtualbox.org/virtualbox/

Il faut cliquer ensuite sur la version de la VirtualBox puis sélectionnez “VBoxGuestAdditions_5.x.x.iso”.

Dans la VM Debian, il faut ouvrir un terminal:

  1. Cliquer sur Activities en haut à gauche puis écrire terminal et cliquer sur l’icone du terminal:
    Ouverture d’un terminal (1/2)
    Ouverture d’un terminal (2/2)
  2. Taper ensuite su pour se connecter comme utilisateur root.
  3. Entrez ensuite le mot de passe de l’utilisateur root.
  4. Taper apt-get update pour mettre à jour la liste des packages du gestionnaire de packages Debian APT.

Il faut installer ensuite les packages “build-essential” et “module-assistant”.
Ces packages sont nécessaires pour être capable de compiler les modules du noyau qui sont nécessaires à l’installation de VirtualBox Linux Guest Additions.

Pour installer ces packages, il faut taper:

apt-get install build-essential module-assistant

A cette étape, une question est posée car la quantité de packages à installer est importante.
Il faut répondre en indiquant [Y] puis [entrée]:

Installation des packages “build-essential” et “module-assistant”

Quand l’installation est terminée, il faut configurer le système pour construire les modules du noyau en tapant:

m-a prepare

Lors de cette étape, de nouveaux packages doivent être installés comme les fichers headers du noyau linux.
Il faut répondre en tapant [Y] puis [Entrée].

Execution de l’instruction m-a prepare

Il faut ensuite insérer le disque virtuel de l’image ISO des VirtualBox Guest Additions en allant dans les paramètres de la VM dans VirtualBox:

  1. Clique droit sur la VM puis cliquez sur “Configuration”
  2. Dans l’onglet “Stockage”, sélectionnez “Vide” sous “Controleur: IDE”
  3. Cliquez sur l’icone du disque à droite de la fenêtre puis cliquez sur: “Choisissez un fichier de disque optique virtuel…”
    Sélectionner le disque VirtualBox Guest Additions (1/2)
    Sélectionner le disque VirtualBox Guest Additions (2/2)
  4. Sélectionnez le fichier ISO “VBoxGuestAdditions-5.x.x.iso” puis validez en cliquant sur “OK”.

A cette étape, le disque devrait être monté dans la VM. Si ce n’est pas le cas, on peut taper la commande suivante pour le monter:

mount /media/cdrom

Il faut ensuite exécuter un script pour compiler les modules additionnels correspondant au noyau en tapant:

sh /media/cdrom/VBoxLinuxAdditions.run
Exécution du script de compilation des modules additionnels

On redémarre la VM en tapant:

reboot

On peut maintenant facilement paramétrer la résolution en cliquant sur “Activities” en haut à gauche et en tapant settings:

Accéder au panneau “Settings”
  1. Cliquer ensuite “Settings” puis “Displays”.
  2. Sélectionnez l’écran en cliquant dessus.
  3. Cliquer sur la résolution qui convient et valider en cliquant sur “Apply”.
  4. Sélection de la résolution

On peut enlever le disque virtuel en tapant sur un terminal en tant qu’utilisateur root:

eject /media/cdrom

A la fin de cette étape, la VM est opérationnelle et utilisable en mode plein écran.

Pour tirer partie de la flexibilité de la VM, on peut la cloner en effectuant un clique droit sur son icone et en cliquant ensuite sur “Cloner…”.
On peut aussi exporter une VM en cliquant sur “Fichier” puis “Exporter un appareil virtuel…”.

L’intérêt de cloner ou d’exporter une VM est de pouvoir repartir d’une version “vierge” lors des différents tests d’installation. Cette possibilité peut s’avérer très utile si on se trompe d’installation ou si on doit repartir de zéro.

Il faut noter que si on supprime la VM dans VirtualBox, on ne pourra pas la réintégrer par le suite. Il est donc préférable de cloner ou d’exporter une VM pour la réutiliser par la suite.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min: références

Références

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min

Le but de cet article est de présenter succinctement les fonctionnalités principales de Powershell de façon à pouvoir écrire des scripts et tirer rapidement partie de ce langage.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min: aide, gestion d’erreurs et manipulation de fichiers (partie 4)

Cet article liste quelques fonctionnalités un peu plus avancées de Powershell pour traiter quelques cas d’implémentation courants.

Messages d’aide

Pour obtenir de l’aide sur une fonction, on peut utiliser l’instruction:

Get-Help <nom de la fonction>

Pour créer des messages d’aide lorsqu’on définit une fonction, on peut utiliser des commentaires déclarés avec une syntaxe particulière. Ces commentaires seront affichés suivant les paramètres utilisés à l’exécution de la fonction Get-Help.

Par exemple:
On peut indiquer dans le corps de la fonction les commentaires:

  • # .SYNOPSIS pour indiquer un texte décrivant brièvement la fonction
  • # .DESCRIPTION permet de décrire la fonction
  • # .PARAMETER donnera des indications sur les paramètres de la fonction
  • # .EXAMPLE indiquera un exemple

Il est possible d’indiquer d’autres informations:

  • # .INPUTS
  • # .OUTPUTS
  • # .NOTES
  • # .LINK

Dans le corps de la fonction, ces commentaires doivent être indiqués de cette façon:

function NomFonction($arg1, $arg2)
{
<#

.SYNOPSIS
Texte correspondant au synopsis 

.DESCRIPTION
Texte correspondant à la description 

.PARAMETER arg1
Aide concernant l'argument 1

.PARAMETER arg2
Aide concernant l'argument 1

.EXAMPLE
Texte correspondant à l'exemple 

.NOTES
Notes diverses 

.LINK
Liens éventuels

.INPUTS
Indication pour les données en entrée

.OUTPUTS
Indication pour les données en sortie

#>

  Write-Host Argument 1: $arg1
  Write-Host Argument 2: $arg2
}

Pour obtenir la documentation complète d’une fonction, il faut utiliser l’option -full:

Get-Help <nom de la fonction> -full

Dans le cas de l’exemple, pour afficher une documentation partielle, on peut exécuter:

PS C:\> Get-Help NomFonction

NAME
    NomFonction
    
SYNOPSIS
    Texte correspondant au synopsis
    
    
SYNTAX
    NomFonction [[-arg1] <Object>] [[-arg2] <Object>] [<CommonParameters>]
    
    
DESCRIPTION
    Texte correspondant à la description
    

RELATED LINKS
    Liens éventuels 

REMARKS
    To see the examples, type: "get-help NomFonction -examples".
    For more information, type: "get-help NomFonction -detailed".
    For technical information, type: "get-help NomFonction -full".
    For online help, type: "get-help NomFonction -online"

Dans le cas de la documentation complète, le résultat est:

PS C:\> Get-Help NomFonction -full

NAME
    NomFonction
    
SYNOPSIS
    Texte correspondant au synopsis
    
    
SYNTAX
    NomFonction [[-arg1] <Object>] [[-arg2] <Object>] [<CommonParameters>]
    
    
DESCRIPTION
    Texte correspondant à la description
    

PARAMETERS
    -arg1 <Object>
        Aide concernant l'argument 1
        
        Required?                    false
        Position?                    1
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    -arg2 <Object>
        Aide concernant l'argument 1
        
        Required?                    false
        Position?                    2
        Default value                
        Accept pipeline input?       false
        Accept wildcard characters?  false
        
    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see 
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216). 
    
INPUTS
    Indication pour les données en entrée
    
    
OUTPUTS
    Indication pour les données en sortie
    
    
NOTES
    
    
        Notes diverses
    
    -------------------------- EXAMPLE 1 --------------------------
    
    PS C:\>Texte correspondant à l'exemple
    
    
    
    
    
    
    
RELATED LINKS
    Liens éventuels

Gestion des erreurs

Pour gérer n’importe quel type d’erreurs qui pourrait intervenir dans la fonction, il faut utiliser le mot clé trap. Par exemple:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
   }
   
   Write-Host Exécuté après "trap"
}

Dans ce cas, il n’y a pas d’erreurs donc le contenu de la partie trap ne sera pas exécutée:

PS C:\> NomFonction
Exécuté avant trap
Exécuté après trap

Dans le cas d’une erreur, le contenu de la partie trap est exécutée:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
   }
   # ATTENTION: l'exécution continue après le "trap"

   # L'erreur est une division par zéro
   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution est:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Tentative de division par zéro.
At line:12 char:4
+    $arg= 3/0
+    ~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException
 
Exécuté après trap

break

Comme indiqué en commentaire de l’exemple précédent, l’exécution se poursuit après l’erreur. Pour stopper l’exécution à la ligne où l’erreur se produit, il faut ajouter break dans le bloc trap:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
      break
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

En exécutant, on obtient:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Tentative de division par zéro.
At line:12 char:4
+    $arg= 3/0
+    ~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException

continue

A l’opposé de l’exemple précédent, si on veut que l’exécution se poursuive après la ligne où l’erreur s’est produite, il faut utiliser le mot clé continue. Avec continue, il n’y aura pas de message d’erreurs:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host Une erreur est survenue
      continue
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

En exécutant la fonction, on obtient:

PS C:\> NomFonction
Exécuté avant trap
Une erreur est survenue
Exécuté après trap

Pour afficher l’erreur avec un texte choisi, on peut utiliser $_:

function NomFonction()
{
   Write-Host Exécuté avant "trap"

   trap 
   {
      Write-Host L`'erreur survenue est:
      Write-Host $_
      continue
   }
   # ATTENTION: l'exécution continue après le "trap"

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution dans ce cas est:

PS C:\> NomFonction
Exécuté avant trap
L'erreur survenue est:
Tentative de division par zéro.
Exécuté après trap

Spécialiser l’erreur attrapée par “trap”

Pour spécialiser l’erreur attrapée par un bloc trap, il faut préciser le type d’exception comme le bloc try...catch en C#:

function NomFonction()
{
Write-Host Exécuté avant "trap"

   trap [System.DivideByZeroException]
   {
      Write-Host L`'erreur est:
      Write-Host $_
      continue
   }

   $arg= 3/0

   Write-Host Exécuté après "trap"
}

Le résultat de l’exécution est similaire aux exemples précédents:

PS C:\> NomFonction
Exécuté avant trap
L'erreur est:
Tentative de division par zéro.
Exécuté après trap

En faisant cohabiter avec une autre bloc trap plus général:

function NomFonction()
{
Write-Host Exécuté avant "trap"

   trap [System.DivideByZeroException]
   {
      Write-Host L`'erreur est:
      Write-Host $_
      continue
   }
   trap 
   {
      Write-Host Autre erreur est:
      Write-Host $_
      continue
   }

   $unknownParameter = "chaine"
   $unknownParameter = 4 + $unknownParameter

  Write-Host Exécuté après "trap"
}

Pour la fonction précédente, l’erreur n’est pas une division par zéro donc c’est le 2e bloc trap qui sera exécuté:

PS C:\> NomFonction
Exécuté avant trap
Autre erreur est:
Cannot convert value "chaine" to type "System.Int32". Error: "Le format de la chaîne d'entrée est incorrect."
Exécuté après trap

Manipulation de fichiers

Pour lire le contenu d’un fichier:

Get-Content "<chemin du fichier>"

Assigner le contenu d’un fichier à une variable:

$val=Get-Content "<chemin du fichier>"

Dans le cas d’un fichier qui contient les lignes suivantes:

First line
Second line
Third line

Si on écrit la ligne suivante, le résultat est:

PS C:\> Write-Host $val
First line Second line Third line

Le résultat est un tableau contenant toutes les lignes. Chaque ligne est accessible en écrivant:

$val[0]

Dans le cas de l’exemple précédent:

Write-Host $val[0]
First line

Pour combiner les éléments du tableau dans une chaîne de caractères unique:

$separateur = [System.Environment]::NewLine
$result = [String]::Join($separateur, $val)

$result est donc une chaîne de caractères contenant tous les éléments du tableau.

Si on affiche son contenu, on obtient:

Write-Host $result
First line
Second line
Third line

Lister des fichiers dans un répertoire

Pour afficher des informations sur liste de fichiers dans un répertoire, on utilise Get-ChildItem. Dans le cas d’un seul fichier, on utilise Get-Item. On peut sélectionner les informations à afficher avec les paramètres suivants:

  • -Name: pour afficher le nom du fichier
  • -Recurse: pour parcourir un répertoire récursivement
  • -Path: pour afficher le chemin du fichier.

Par exemple pour afficher le chemin des fichiers se trouvant dans le répertoire courant (Get-Location renvoie le chemin du répertoire courant):

Get-ChildItem -Path (Get-Location).Path

Manipulations basiques sur des fichiers

Quelques cmdlets permettent d’effectuer des opérations basiques sur les fichiers:

  • Remove-Item: pour supprimer un fichier ou un répertoire
  • Rename-Item: pour renommer un fichier ou un répertoire
  • Move-Item: pour déplacer un fichier ou un répertoire

Pour créer un répertoire on peut écrire la commande suivante:

New-Item -Path "<chemin du répertoire>" -ItemType Directory

Générer un fichier

Pour écrire un fichier, on peut écrire:

Set-Content -Value <variable avec le contenu à écrire> -Path "<chemin du fichier à écrire>"

Si le fichier existe déjà, Set-Content écrase son contenu, il ne rajoute d’élément à un contenu déjà existant.

Pour ajouter un contenu quand le fichier existe déjà, il faut utiliser Add-Content.

Par exemple:

Add-Content -Value <nom d'une variable avec le contenu à écrire> -Path "<chemin du fichier>"

Si le fichier n’existe pas, il sera créé.

On peut aussi utiliser la commande Out-File.
Par exemple pour générer un fichier temporaire:

$tempFilePath = [System.IO.Path]::GetTempFileName()
$fileContent = "Content to write to the temp file"
$fileContent | Out-File $tempFilePath

Exporter dans un fichier CSV

On peut exporter un fichier CSV avec des instructions particulières, par exemple:

Get-Process | Export-Csv "<nom du fichier>"

Ajouter un header au fichier CSV au moment d’importer le fichier:

$header = "col1", "col2", "col3"
$process = Import-Csv "<nom du fichier à importer>" -Header $header

Fichier XML

De même pour créer un fichier XML, on peut écrire:

$courseTemplate = @"
<employees>
  <employee id="1">
    <name>Person 1</name>
    <age>23</age>
  </employee>
  <employee id="2">
    <name>Person 2</name>
    <age>56</age>
  </employee>
  <employee id="3">
    <name>Person 3</name>
    <age>21</age>
  </employee>
</employees>
"@

$courseTemplate | Out-File "<chemin du fichier>"

Si on affiche le contenu de la variable, on s’aperçoit qu’il s’agit d’un type plus complexe qu’une chaîne de caractères:
Par exemple:

PS C:\> Write-Host $xmlVar
#document

En omettant Write-Host, on peut afficher davantage d’éléments:
Par exemple:

PS C:\> $xmlVar
employees
---------
employees

et

PS C:\> $xmlVar.employees
employee                      
--------                      
{Person 1, Person 2, Person 3}

Pour charger un fichier XML
On peut écrire le code suivant:

$xmlVar = New-Object xml 
$xmlVar.Load("<chemin du fichier à lire>")

Travailler sur un nœud XML
On peut atteindre un nœud particulier d’un fichier XML facilement de la façon suivante:

$nodeVal = (@($xmlVar.employees.employee)[0]).Clone()

employees.employee correspond à un élément du fichier; (@($xmlVar.employees.employee) contiendra un tableau avec toutes les occurences de l’élément.

Ainsi, pour afficher le contenu de $nodeVal:
Par exemple:

PS C:\> $nodeVal
id name     age
-- ----     ---
1  Person 1 23 

Pour ajouter des éléments XML
A partir de la variable définit précédemment, on peut éditer des valeurs et ajouter un nouveau nœud en écrivant:

$newVal = $nodeVal.Clone()
$newVal.name = "Person 4"
$newVal.age = "3"
$xmlVar.employees.AppendChild($newVal) > $null

Si on ne rajoute pas > $null dans la ligne .AppendChild($newVal), l’ajout sera redirigé vers l’écran.

Si on affiche de nouveau, le contenu de $xmlVar.employees, on s’aperçoit qu’un nœud a été rajouté:
Par exemple:

PS C:\> $xmlVar.employees
employee                      
--------                      
{Person 1, Person 2, Person 3, Person 4}

Pour supprimer le template du fichier ou supprimer un nœud du fichier:

$xmlVar.courses.course | 
  Where-Object {$_.Name -eq "Person 1"} |
  ForEach-Object {[void]$_
    $xmlVar.courses.RemoveChild($_)
  }

Pour sauver le fichier XML

$xmlVar.Save("<chemin du fichier>")

Pour charger directement un fichier XML
On peut utiliser le mot clé [xml], par exemple:

[xml]$xmlVar = Get-Content "<chemin du fichier>"
Les autres articles de cette série

Partie 1: exécuter Powershell

Partie 2: les cmdlets

Partie 3: instructions dans des scripts Powershell

Partie 4: aide, gestion d’erreurs et manipulation de fichiers

Références

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min: instructions dans des scripts Powershell (partie 3)

Cet article indique quelques instructions utilisables dans un script Powershell. La liste d’instructions n’est pas exhaustive toutefois elle devrait permettre d’écrire rapidement un script avec les principales instructions.

Quelques instructions courantes utilisables dans les scripts

Ces instructions peuvent être utiles pour implémenter des scripts.

Manipulation de variables

L’affectation de variable se fait en utilisant le caractère $, par exemple:

$var1 = "value 1"
$var2 = $var1

L’affectation de la variable nulle se fait en écrivant:

$var1 = $null

Effectuer des “casts”

On peut effectuer des casts en indiquant le type souhaité avec des crochets [ ]. L’indication du type peut se faire de 2 façons:

# Effectuer un "cast" vers DateTime 
[System.DateTime]$var1="2017-08-25"

# Effectuer un "cast" vers un XmlDocument
$var2 = [System.Xml.XmlDocument]"<xml><node>HERE</node></xml>"
“Cast” implicite

Dans certains cas, powershell effectue implicitement des casts, par exemple si on écrit:

$stringVar = "PowerShell"
$doubleVar = 2.0
$result = $stringVar + $doubleVar

Dans ce cas le cast de $doubleVar en string se fera implicitement.

En revanche, si on écrit:

$result = $doubleVar + $stringVar # Cette ligne occasionne une erreur

Il y aura une erreur car Powershell ne pourra pas convertir $doubleVar en string.

Une solution peut consister à utiliser l’opérateur -as:

$result = ($doubleVar -as [string]) + $stringVar

On peut aussi effectuer le cast explicitement:

$result = [string]$doubleVar + $stringVar

if…then…else

Pour implémenter une clause if...then...else, la syntaxe est semblable au C#:
Par exemple:

$var = 2
if ($var -eq 1)
{ 
   # ... 
}
else
{
   # ... 
}
Pas de elseif

Il n’y a pas de elseif, il faut utiliser des if ... then ... else imbriqués.

Operateurs de comparaison

D’autres opérateurs de comparaison sont disponibles:

  • -eq: comparateur d’égalité
  • -ne: “not equal to”, la valeur est True si les opérandes ne sont pas égales.
  • -ge: “greater than or equal”, la valeur est True si l’opérande de gauche est supérieure ou égale à l’opérande de droite.
  • -gt: “greater than”, la valeur est True si l’opérande de gauche est strictement supérieure à l’opérande de droite.
  • -le: “less than or equal”, la valeur est True si l’opérande de gauche est inférieure ou égale à l’opérande de droite.
  • -lt: “less than”, la valeur est True si l’opérande de gauche est strictement inférieure à l’opérande de droite.
  • -like et -notlike: permet d’effectuer des comparaisons de chaines de caractères en utilisant des wildcards:
    • ? pour désigner un seul caractère non spécifié
    • * pour désigner un ou plusieurs caractères non spécifiés

    Par exemple: $var -like "*uary"

  • -match et -notmatch: permet de vérifier si une chaine de caractères respecte une expression régulière, par exemple: $string -match "\w" (se reporter à Regular Expression Language – Quick Reference pour plus de précisions sur les expressions régulières).
  • -contains et -notcontains: permet de tester si une valeur se trouve dans une liste. Par exemple:
    $names = "val1", "val2", "val3"
    $names -Contains "val2" 
  • -is et -isnot: permet de tester le type d’une variable .NET (même opérateur qu’en C#). Par exemple:
    $stringToTest = "chaine de caracteres"
    ($stringToTest -is [System.String])

Opérateurs logiques

Ces opérateurs permettent de tester plusieurs expressions logiques:

  • -and: opérateur ET
  • -or: opérateur OU
  • -xor: opérateur OU exclusif (retourne True si seulement une expression est vraie)
  • -not: opérateur NOT

Par exemple:

$var1="value1"
$var2="value2"
($var1 -eq "value1") -and ($var2 -eq "value2")

switch

Pour implémenter un switch...case, on utilise la syntaxe suivante:

switch ($var)
{ 
   21 { "value1"; break }
   22 { "value2" }  # pas d'arrêt dans ce cas car pas de break
   23 { "value3"; break }
   default { "default" }
}

On peut executer une liste de type:

switch (1, 2, 3, 0)

Le parcours se fait dans l’ordre des valeurs.

Par défaut la comparaison n’est pas sensible à la casse:

Par exemple:

switch ("AAA")
{
   "aAA" { "OK" }
   "aaA" { "OK" }
   "aaa" { "OK" }
}

Pour que la comparaison soit sensible à la casse, il faut ajouter -casesensitive, par exemple:

switch -casesensitive ("AAA")
{
   # ... 
}

Utilisation de “wildcards”

On peut utiliser des wildcards, par exemple:

switch -wildcard ("AAA")
{
   "AA*" {"OK"}
   "AA?" {"OK"}
   "A??" {"OK"}
}

Définition des listes d’éléments

Arrays

On peut définir des tableaux simples de la façon suivante:

$tab1=@(1, 2, 3, 4, 5, 6)
$tab2=1, 2, 3, 4, 5, 6

L’accès aux éléments du tableau se fait classiquement avec des crochets:

$tab1[3]
# On peut utiliser une variable contenant la valeur d'un index:
$i=2
$tab2[$i]

Listes .NET

On peut utiliser aussi des listes .NET de cette façon:

$dotnetList=New-Object Collections.Generic.List[string]

# Pour ajouter un élément:
$dotnetList.Add("first item") 

# Pour accéder à un élément à un index particulier:
$dotnetList.Item(0)

Table de hashage

Une table de hashage peut se définir de cette façon:

$hashTable= @{
    "key1" = "value1";
    "key2" = "value2";
    "key3" = "value3";
    "key4" = "value4"
}

L’accès à un élément se fait en écrivant: $hashTable["key3"].

Boucles

Boucle “while”

Une boucle while s’implémente de cette façon:

$i = 0
while ($i -le 5)
{
   $i = $i + 1
}

do…while

L’implémentation du do...while se fait de cette façon:

$i = 0
do
{ $i = $i + 1 }     # un équivalent à cette incrémentation est $i++
while ($i -le 5)

do…until

Pour coder une boucle do...until, on peut écrire:

$i = 0
do
{ $i++ }
until ($i -gt 5)

Boucle “for”

La boucle for s’implémente de façon classique:

for ($f = 0; $f -le 5; $f++)
{
   # ... 
}

Boucle “foreach”

De même la boucle foreach est proche de son équivalent en C#:

$array = 1, 2, 3, 4
foreach ($item in $array)
{
   "`$item = $item"
}

On peut utiliser une boucle foreach avec le résultat de cmdlets, par exemple:

Set-Location "<chemin sur le disque>"
foreach ($file in Get-ChildItem)
{
   $file.Name
}

“break” et “continue”

On peut utiliser break comme en C# pour stopper l’exécution d’une boucle:

foreach ($file in Get-ChildItem)
{
   break
}

De même qu’en C#, on peut aussi utiliser “continue” pour passer à l’itération suivante sans exécuter les instructions se trouvant après le continue.

Utilisation de “labels” avec “break” et “continue”

Les labels sont des espèces de “goto” pour indiquer à quel niveau d’une boucle, on souhaite que l’exécution se poursuive. Ces labels permettent d’effectuer des sauts vers une boucle particulière quand on se trouve dans des foreach imbriqués:

Dans l’exemple suivant, le label est outsideloop et break outsideloop permet de stopper de la première boucle foreach:

:outsideloop foreach( ... )
{
   foreach ( ...)
   { 
      break outsideloop
   }
}

L’exécution va donc s’arrêter au niveau de la 1ere boucle.

On peut aussi utiliser des “labels” avec continue:

:outsideloop foreach( ... )
{
   foreach ( ...)
   { 
      continue outsideloop
   }
}

Dans ce cas, l’exécution va continuer au niveau de la première boucle foreach.

Scripts blocks

Un script block est un bloc de code qu’on peut définir au préalable et qui se sera pas exécuté au moment de cette définition. On pourra exécuter ce bloc de code par la suite quand on le désire. L’intérêt des script blocks est de pouvoir définir et exécuter une “mini-fonction” dans le corps même d’une fonction.

Les blocs sont désignés avec des accolades { }.

Pour définir un script block, dans un premier temps on l’affecte à une variable:

$var = { Clear-Host; "Powershell" }

Ensuite dans un 2e temps, pour l’exécuter on peut écrire:

&$var 
# ou directement 
& { .... }

Pour utiliser le résultat de l’exécution d’un script block:

$value = (41 + 1)
1 + (& $value)

Remarque: il faut rejouter des parenthèses et écrire (& $value) pour que le corps du script block soit interprêté.

Dans le cas d’un script block, certains éléments sont consommés et d’autres ne le sont pas.

Par exemple, on écrit:

$value = { 42; Write-Host "Powershell" }

42 n’est pas consommé et il peut être utilisé comme valeur dans une instruction suivante.
En revanche Write-Host "Powershell" est consommé (car affiché au moment de l’exécution).

Si on exécute:

1 + (& $value)

On obtient 43.

De même si on exécute:

$value2 = & $value 

La variable $value2 contient 42.

On peut exécuter un script block avec l’instruction Invoke():

$var = { Clear-Host; "Powershell" }
$var.Invoke()

On peut aussi utiliser la cmdlet Invoke-Command:

$var = { Clear-Host; "Powershell" }
Invoke-Command -ScriptBlock $var

Creation d’un “script block” à partir d’une chaîne de caractères

On peut créer un script block à partir d’une chaîne de caractères en écrivant:

$newScript = [scriptblock]::Create("Get-ChildItem")
# ou
$newScript = [System.Management.Automation.ScriptBlock]::Create("Get-ChildItem")

“return”

En utilisant le mot clé return, on peut stopper l’exécution:

$value = { return 42; Write-Host ... }

Ainsi Write-Host ne sera pas exécuté.

Passage de paramètres à un “script block”

Pour passer des paramètres à un script block, il existe 2 méthodes:

Méthode 1: collection d’arguments

Les arguments sont passés dans une collection, dans l’exemple la collection est $arg:

$qa = { $question = $args[0] 
   $answer = $args[1]
}

Si on exécute l’instruction:

& $qa "value1" "value2"

alors arg[0] est égal à value1 et arg[1] est égal à value2.

Avec l’instruction Invoke() avec des arguments, la syntaxe est:

$qa.Invoke("value1", "value2")

Avec Invoke-Command:

Invoke-Command -ScriptBlock $qa -ArgumentList 'value1','value2'

Méthode 2: utiliser “param”

En utilisant le mot clé param on peut définir une liste d’arguments, par exemple:

$qa = { param( $question, $answer )
  Write-Host $question
  Write-Host $answer
}

A l’exécution, pour passer les paramètres la syntaxe est similaire à la méthode de la collection d’arguments:

& $qa "value1" "value2"

On peut aussi utiliser la syntaxe suivante:

& $qa -question "value1" -answer "value2"

On peut aussi utiliser seulement les initiales des paramètres:

& $qa -q "value1" -a "value2"

La syntaxe avec Invoke-Command est similaire à la méthode précédente:

Invoke-Command -ScriptBlock $qa -ArgumentList 'value1','value2'

Vérifier qu’un argument est manquant

Pour vérifier si un argument est manquant, on peut écrire:

$qa = { param($question, $answer )
   if (!$answer)  # si la variable n'est pas affectée
   {
     # ... 
   }
}

Valeur par défaut

Pour affecter une valeur par défaut à un argument, on peut écrire:

$qa = { param($question, $answer = "default value" )
   # ...
}

Forcer le type des paramètres:

$point = { param([int] $x, [int] $y)
   # ...
}

“script block” et “pipelines”

Il est possible d’utiliser des script blocks avec des pipelines. Toutefois les pipelines se définissent dans ce cas en utilisant des mot clés process, begin et end qui seront utilisés pour désigner des blocs de code à exécuter:

  • process: bloc de code qui constitue le corps du script block dans le pipeline.
  • begin: bloc de code qui sera exécuté avant d’exécuter le corps du script block dans le pipeline (i.e. ce bloc sera exécuté avant celui de process).
  • end: bloc de code qui sera exécuté après d’exécuter le corps du script block dans le pipeline (i.e. ce bloc sera exécuté après celui de process).

Par exemple:

$pipeline = { 
   process { 
      if ($_.Name -like "*.ps1" )
      { 
         return $_.Name 
      }
   }
}

Le mot clé process est utilisé pour indiquer les instructions qui seront exécutées à l’exécution du script block dans le pipeline.

Par exemple pour exécuter le script block définit dans la variable $pipeline dans un pipeline, on peut écrire:

Get-ChildItem | &$pipeline

En utilisant begin et end pour définir des instructions à exécuter avant et après l’exécution le script block dans un pipeline:

$pipeline = { 
   begin { $val = "value1" }
   process { 
      if ($_.Name -like "*.ps1" )
      { 
         return $_.Name 
      }
   }
   end { return $val }
}

Mot clé “param” avec les “script blocks”

On peut aussi utliser le mot clé param pour un script bloc utilisé dans un pipeline:

$pipeline = { 
   param ( $headerText )
   begin { ... }
   process { ... }
   end { ... }
}

Par exemple si on exécute le code suivant:

$pipeline = { 
   param ( $headerText )
   begin { Write-Host "Executé avant process: $headerText" }
   process { Write-Host "Execution de process: $headerText" }
   end { Write-Host "Executé après process: $headerText" }
}

& $pipeline "value1"

Le résultat sera:

Executé avant process: value1
Execution de process: value1
Executé après process: value1

Portée des variables dans un “script block”

Le terme employé dans la documentation pour qualifier la portée des variables est “scope”.

Quelques règles s’appliquent aux variables:

  • Les variables déclarées à l’extérieur du script block sont utilisables dans le script block.
  • Les variables modifiées dans le script block ont une portée limitée au script block. La valeur ne sera pas modifiée à l’extérieur du script block.

Portée des paramètres dans un “script block”

Le paramètre -scope utilisé dans un script block avec les cmdlets Get-Variable et Set-Variable permettent d’indiquer des perimètres de portée. Ainsi suivant le périmètre qu’on a définit, le comportement différe du comportement par défaut.

Par exemple, en utilisant -scope 1 on indique que l’on souhaite avoir la valeur du parent du script block:

$var = 42
& { $var = 33; 
     Write-Host "$var" 
     Write-Host "Parent: " (Get-Variable var -valueOnly -scope 1 )
}

Dans ce cas la valeur de "$var" sera 33 alors que la valeur de "Parent: " (Get-Variable var -valueOnly -scope 1 ) sera Parent: 42

De même avec Set-Variable, -scope 1 permet d’indiquer qu’on souhaite modifier la valeur de la variable au niveau du parent:

$var = 42
Write-Host "Valeur au niveau parent: $var"
& { Set-Variable var 33 -scope 1
    Write-Host "Valeur dans le script block: $var"
}

Write-Host "Valeur après le script block: $var"

Le résultat de cette exécution est:

Valeur au niveau parent: 42
Valeur dans le script block: 33
Valeur après le script block: 33

Utiliser -scope 1 n’est pas trés clair, il est préférable d’utiliser global ou private.

Utiliser “$global” pour modifier une variable de façon globale

Le mot clé $global permet de modifier la valeur d’une variable de façon globale. En reprenant l’exemple précédent:

$var = 42
Write-Host "Valeur au niveau parent: $var"
& { $global:var = 33
write-host "Valeur dans le script block: $var" }

Write-Host "Valeur après le script block: $var"

Le résultat de cette exécution est:

Valeur au niveau parent: 42
Valeur dans le script block: 33
Valeur après le script block: 33

$global: permet d’indiquer qu’on souhaite modifier la valeur du parent. Il s’utilise avec les deux points : et sans le $.

Utiliser “$private” pour modifier une variable de façon locale

Avec le mot clé $private la valeur d’une variable ne sera pas affectée dans le script block, par exemple:

$value = 42
Write-Host "Valeur au niveau parent: $value"
& { $private:value = 21
Write-Host "Valeur dans le script block: $value" }

Write-Host "Valeur après le script block: $value"

Le résultat est:

Valeur au niveau parent: 42
Valeur dans le script block: 21
Valeur après le script block: 42

Dans ce cas la valeur de $value n’est pas modifiée que dans le script block.

Fonctions

Les fonctions sont comme les script blocks sauf qu’elles sont définies de façon plus globale et non à l’intérieur d’une autre fonction.

Pour définir une fonction:

function NomFonction ( $value1, $value2)
{
  # ... 
  Write-Host $value1
  Write-Host $value2
}

Pour appeler la fonction:

NomFonction "val1" "val2"
# ou 
$par1 = "val1"
$par2 = "val2"
NomFonction $par1 $par2

On n’utilise pas de parenthèses pour indiquer les paramètres lors de l’appel, on indique directement les paramètres séparés d’un espace.

Passer des variables par référence

Pour qu’une variable soit passé en argument par référence, on utilise le mot clé [ref]. L’intérêt de passer une variable par référence est de pouvoir modifier sa valeur dans le corps de la fonction et de l’utiliser à l’extérieur de celle-ci.

Par exemple dans le code suivant, la valeur de la variable $par1 sera 33 à l’extérieur du corps de la fonction après exécution de cette fonction:

function NomFonction ([ref] $par1)
{
   $par1.Value = 33
   Write-Host "Valeur modifiée par la fonction: "$par1.Value
}

Si on exécute la fonction précédente de cette façon:

$value = 23
Write-Host "Valeur avant la fonction: $value"
NomFonction([ref] $value)
Write-Host "Valeur après la fonction: $value"

Le résultat est:

Valeur avant la fonction: 23
Valeur modifiée par la fonction: 33
Valeur après la fonction: 33

Il faut ajouter .value car avec [ref] powershell considère que la variable est de type object.

Pour appeler une fonction avec [ref], on écrit:

NomFonction([ref] $var)

Ainsi si on utilise [ref] et si la fonction change la valeur de la variable, la valeur sera modifiée à l’extérieur de la fonction.

Les “pipelines” dans une fonction

La syntaxe est la même que pour les script blocks. Il faut utiliser les mot clés process, begin et end pour désigner des blocs de code à exécuter:

  • process: bloc de code qui sera exécuté dans le pipeline.
  • begin: bloc de code qui sera exécuté avant le bloc process.
  • end: bloc de code qui sera exécuté après le bloc process.

Par exemple si on définit la fonction:

function NomFonction()
{
   begin { Write-Host "Exécuté avant process" }
   process { Write-Host "Exécution de process" }
   end { Write-Host "Exécuté après process" }
}

Et si on exécute la fonction avec:

NomFonction

Le résultat est:

Exécuté avant process
Exécution de process
Exécuté après process

Filtres

Les filtres sont des espèces de fonctions sans paramètres. L’intérêt des filtres est de les utiliser dans des pipelines.

Pour définir un filtre, on utilise le mot clé filter:

filter NomFiltre
{
   # ...
   Write-Host $_.Name
}

$_ permet d’utiliser la valeur d’un élément à une itération du pipeline.

On peut combiner des fonctions et des filtres dans un pipeline.

Par exemple, si on exécute la ligne suivante:

Get-ChildItem | NomFiltre

On obtient seulement le noms des fichiers se trouvant dans le répertoire courant.

Utiliser des “switch”

Les switch permettent d’indiquer des paramètres facultatifs dans une fonction sous la forme de booléen. Quand ces paramètres ne sont pas renseignés, ils n’ont pas de valeur. Les switch ne sont pas des paramètres facultatifs mais seulement des booléens qui peuvent être utilisés pour activer ou désactiver une fonctionnalité.

Par exemple, en utilisant le mot clé switch devant les paramètres $verbose et $debug on peut indiquer que ces paramètres sont des booléens facultatifs:

function NomFonction()
{
   param([switch] $verbose, [switch] $debug)
   if ($verbose.IsPresent)
   {
      Write-Host "Verbose est présent"
   }
   else
   {
      Write-Host "Verbose n'est pas présent"
   }
}

$verbose.IsPresent permet de tester si la variable $verbose a une valeur ou non.

Pour appeler une fonction qui utilise des switch, on peut écrire:

NomFonction -verbose -debug

Dans ce cas là, le résultat est:

Verbose est présent

Si on exécute la ligne suivante:

NomFonction -debug

Le résultat sera:

Verbose n'est pas présent

L’article suivant permet de détailler la syntaxe pour afficher des messages d’aide, de gérer des erreurs dans les fonctions et comporte des exemples pour manipuler des fichiers.

Les autres articles de cette série

Partie 1: exécuter Powershell

Partie 2: les cmdlets

Partie 3: instructions dans des scripts Powershell

Partie 4: aide, gestion d’erreurs et manipulation de fichiers

Références

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min: les cmdlets (partie 2)

Le but de cet article est de détailler l’utilisation des commandes Powershell. Ces commandes s’appellent des “command-lets” mais l’abbréviation cmdlets est couramment utilisée pour les désigner.
Les cmdlets peuvent être exécutées dans une console Powershell directement ou dans un script .ps1.

Les commandes disponibles en Powershell sont plus nombreuses et plus perfectionnées que les commandes batch classiques:

  • Commandes batch: on peut utiliser les mêmes commandes batch en powershell comme: cd, dir, copy etc…
  • Commandes Linux: on peut utiliser des commandes provenant de Linux comme: cd, ls, cp etc…
  • Commandes cmdlet: ce sont des commandes spécifiques à Powershell (cmdlet se prononce “command-let”).

La syntaxe des cmdlets est souvent la même c’est-à-dire:

<Verbe>-<Nom>

On peut trouver le verbe et nom en minuscules ou avec la première lettre en majuscule comme, par exemple:
Get-Help, Get-Command etc…

Powershell n’est pas sensible à la casse

Powershell n’est pas sensible à la casse, les commandes écrites en majuscules ou en minuscules seront interprétées de la même façon. De même, Powershell n’est pas sensible aux espaces ou tabulations.

Dans le cas où la cmdlet comporte un argument sous forme d’option, la syntaxe que l’on peut rencontrer peut être de la forme:

<verbe>-<nom> -<nom_option> <valeur de l'option>

Par exemple:

Start-Service -name eventlog

PowerShell Module Browser

Microsoft a mis en place un browser capable d’afficher de l’aide sur toutes les commandes Powershell: PowerShell Module Browser.
Ce browser est particulièrement efficace puisqu’il permet d’accéder aux pages d’aides pour toutes les commandes et pour toutes les versions de Powershell.
On peut, d’autre part, restreindre la recherche à une version spécifique en cliquant sur “Tous les paquets” et en sélectionnant la version adéquate.

Alias

Il est possible d’utiliser des alias pour raccourcir le nom des cmdlets. Ces alias ne sont pas forcément clairs toutefois ils permettent de raccourcir des cmdlets fréquemment utilisées.

Afficher les “alias”

Get-Alias permet d’afficher tous les alias.

Get-ChildItem est un équivalent de ls.

Paramétrer un “alias” personnalisé

On peut paramètrer des alias personnalisés en utilisant la syntaxe suivante:

Set-Alias <nom de l´alias> <commande à exécuter>

Par exemple:

Set-Alias list Get-ChildItem

La durée de vie de l’alias est égal à celle de l’invite de commande Powershell. Si on la ferme, l’alias est perdu.

Persister un “alias”

Pour persister un alias, on peut utiliser la commande Export-Alias:

Export-Alias "<fichier CSV où l´alias sera sauvegardé>" <nom de l´alias>

Récupérer un “alias” sauvegardé

Pour récupérer un alias sauvegardé dans un fichier CSV:

Import-Alias "<chemin du fichier CSV où l´alias a été sauvegardé>" 

Quelques exemples de “cmdlets”

Afficher toutes les commandes

Pour afficher toutes les commandes disponibles:

Get-Command

On peut ajouter des arguments pour chercher suivant un critère particulier:

Get-Command –verb "<nom du verbe>" 
Get-Command –noun "<nom à chercher>"

Changer le répertoire courant

Pour changer le répertoire courant:

Set-Location "<chemin du répertoire>" 

Plus simplement on peut utiliser les alias proches des commandes utilisées sur Linux ou sur le batch comme cd.

Lister le contenu d’un répertoire

Pour avoir le contenu d’un répertoire:

Get-ChildItem

L’alias pour cette commande est ls.

Afficher de l’aide

Pour afficher de l’aide sur les commandes, on peut écrire:

Get-Help <nom de la commande>

Pour afficher l’aide concernant une commande avec des exemples, on peut écrire:

Get-Help <nom de la commande> -examples

On peut aussi utiliser pour avoir de l’aide:

<nom de la commande> -?

Par exemple:

Get-Command -? 

Effacer le contenu d’une console

Pour effacer le contenu de la ligne de commandes:

Clear-Host

Afficher l’historique de la console

Il suffit de taper:

Get-History

Avoir la liste des processus

Pour avoir la liste des processus en cours d’exécution sur la machine, on peut exécuter:

Get-Process

Lancer un processus

Pour lancer un processus, on peut utiliser la commande Start-Process avec le chemin de l’exécutable. En utilisant cette commande avec un fichier, un programme par défaut se lancera suivant l’extension
de ce fichier, par exemple:

$filePath = "example.csv" 
$process = Start-Process $filePath

On peut aussi lancer un programme avec des arguments de cette façon:

$args = "customFile.txt"
& 'C:\Windows\System32\notepad.exe' $args

Référencer un autre fichier .ps1

Pour référencer à un autre fichier de scripts .ps1 de façon à utiliser les fonctions qu’il contient, on peut écrire:

Import-Module "<chemin du fichier .ps1>"

Retarder l’exécution

On peut mettre en pause l’exécution pendant quelques secondes en écrivant:

Start-Sleep -Seconds 3
Ecrire une commande sur plusieurs lignes

On peut écrire une commande sur plusieurs lignes en utilisant le caractère ` (accent grave), ce caractère est accessible avec la combinaison [AltGr] + [7].

Par exemple si on écrit la commande suivante, elle sera interprétée en une seule ligne:

Get-Process | `
where-Object {$_.Name -like "powershell"} | `
Select-Object Id

Mettre en forme des données

Certaines commandes renvoient des listes d’éléments, il est possible de mettre en forme ces éléments pour rendre plus lisible leur lecture. Cette mise en forme peut se faire en utilisant:

  • Format-List: pour lister tous les éléments sous la forme d’une liste avec une seule colonne.
  • Format-Table: pour lister les éléments sous la forme d’un tableau avec plusieurs colonnes.
  • Format-Wide: pour lister les éléments sous la forme de plusieurs colonnes, on peut choisir le nombre de colonnes.

Par exemple:

Get-Process | Format-List
Get-Process | Format-Table 

# Pour mettre en forme sur 3 colonnes
Get-Process | Format-Wide -Column 3

Chaines de caractères

Comme en .NET, les chaines de caractères sont identifiées par des quotes:

$var="classic string"

Par défaut, les variables utilisées dans les chaines de caractères avec des double quotes (cf. " ") sont remplacées par leur valeur. Ainsi si on écrit:

$a="powershell"
Write-Host "classic $a string"

On aura:

classic powershell string

Pour que les variables à l’intérieur d’une chaine de caractères ne soient pas interprétées, il faut définir la chaine avec quotes simples (cf. ' '):

$b='classic $a string'
Write-Host $b

Le résultat sera:

classic $a string

Pour définir une chaine de caractères sur plusieurs lignes, on utilise les caractères @. Les variables se trouvant dans une chaine définie avec @ sont interprétées.

Par exemple:

$bigString= @"
      Ligne 1
      Ligne 2
      Variable: $a
      Ligne 3 etc...
"@
Write-Host $bigString

Le résultat sera:

      Ligne 1
      Ligne 2
      Variable: powershell
      Ligne 3 etc...

Pour ne pas interpréter les variables avec l’utilisation du caractère @, il faut précéder la variable avec ` (` est le caractère accent grave accessible avec [AltGr] + [7]). Avec la chaine précédente, on aurait:

$bigString= @"
      Ligne 1
      Ligne 2
      Variable: `$a
      Ligne 3 etc...
"@
Write-Host $bigString

Dans ce cas le résultat sera:

      Ligne 1
      Ligne 2
      Variable: $a
      Ligne 3 etc...

Retour à la ligne

Pour effectuer des retours à la ligne dans une chaine de caractères, on peut utiliser `n (` est le caractère accent grave accessible avec [AltGr] + [7]):

$c="Retour à la ligne: `n Nouvelle ligne"
Write-Host $c

Le résultat sera:

Retour à la ligne:
Nouvelle ligne
Tabulation

On peut aussi noter le caractère `t pour désigner une tabulation dans une chaine de caractères.

Utiliser le Framework .NET

Powershell est basé sur .NET. Tout est un objet .NET. Ainsi, beaucoup d’objets sont disponibles en Powershell de la même façon qu’en .NET.

Par exemple si on affecte une chaine de caractères $a = "powershell":

  • $a permet d’obtenir la valeur de la variable
  • $a.Length permet d’obtenir la longueur de la chaine
  • $a.GetType() permet d’obtenir le type de la valeur de la variable.

Accès aux objets statiques

Powershell permet d’appeler des objets .NET statiques ce qui étend davantage ses fonctionnalités. Pour appeler une méthode du framework .NET on peut taper une instruction de type:

[<objet du framework .NET>]::<nom de la méthode>

Par exemple pour appeler la méthode statique suivant qui se trouve dans le namespace System.Reflection:

public static Assembly Load(string assemblyString)

On peut utiliser la syntaxe:

[System.Reflection.Assembly]::Load("System.Web")

Instanciation d’objets

L’instanciation de nouveaux objets .NET se fait en utilisant New-Object, par exemple:

$xmlDoc = New-Object System.Xml.XmlDocument

Utiliser des “pipelines”

Les pipelines permettent d’utiliser plusieurs commandes successivement suivant un pipeline de commandes.

Par exemple, pour avoir la liste des fichiers dont la taille est supérieure à 100kb:

Get-ChildItem | Where-Object { $_.Length -gt 100kb }

$_ permet de spécifier l’élément courant lors de l’exécution en boucle des éléments renvoyés par Get-ChildItem.

Pour indiquer la taille, on peut utiliser:

  • Kb pour kilo bytes
  • Mb pour mega bytes
  • Gb pour giga bytes

Pour trier par ordre alphabétique:

Get-ChildItem | Where-Object { $_.Length -gt 100kb } | Sort-Object Length 

Pour formatter la sortie de Get-ChildItem:

Get-ChildItem | `
  Where-Object { $_.Length -gt 100kb } | `
  Sort-Object Length | `
  Select-Object Name, Length

Autre exemple permettant de lister les ID des processus dont le nom contient le mot “powershell”:

Get-Process | `
  where{$_.Name -like "powershell"} | `
  Select Id

Powershell “providers” (utilisation des “snap-ins”)

Un provider est une bibliothèque .NET qui permet de mettre à disposition des commandes de façon à pouvoir naviguer dedans. On peut utiliser les mêmes commandes pour naviguer dans les différents types d’objets mis à disposition dans le provider.

Get-PSProvider permet d’avoir tous les providers disponibles par défaut.
Tous les providers sont utilisables sur des drives. C’est à travers ces drives qu’on peut naviguer et retrouver les données.

Get-Psdrive permet d’afficher tous les drives.

Par exemple, si on se place dans le drive consacré à l’environnement on peut explorer le contenu des variables d’environnement.
Pour se placer dans le drive de l’environnement, il faut exécuter:

Set-Location env:

On peut alors lister les variables d’environnement en écrivant:

Get-ChildItem 

Comme précédemment, on peut formater les données en faisant:

Get-ChildItem | Format-Table –Property Name, Value –Autosize 

Pour avoir la liste des alias, on peut écrire:

Set-Location alias: 
Get-ChildItem

Pour revenir au drive correspondant au système de fichiers, il suffit de se placer dans le drive d’un volume (par exemple le disque C):

Set-Location c: 

Ajouter un “provider”

Pour ajouter un provider, on effectue cette opération en utilisant des snap-ins:

Pour lister tous les snap-ins disponibles (c’est-à-dire enregistrables), on exécute:

Get-PSSnapin

Pour lister les snap-ins enregistrés:

Get-PSSnapin –Registered

Ces snap-ins sont enregistrés mais non exécutés.

Pour charger un snap-in qui est enregistré:

Add-PSSnapin <nom de l'addin>

On peut vérifier qu’ils sont bien chargés en exécutant:

Get-PSSnapin –Name <nom de la commande>

Quand on rajoute un snap-in, il ajoute un drive et on peut accéder à ce drive de la même façon que pour les autres exemples:

Set-Location <nom du drive>:
Get-ChildItem

Quelque soit le provider qu’on utilise, on y accéde de la même façon en utilisant les commandes Set-Location et Get-ChildItem.
D’autre part, la navigation dans les données mises à disposition pour le provider se fait de la même façon que la navigation dans un système de fichiers:

  • Il y a une notion de hiérarchie
  • On peut indiquer l’élément dans lequel on veut naviguer en exécutant Set-Location
  • On obtient la liste des éléments en exécutant Get-ChildItem.

Supprimer un “provider”

On exécute une commande permettant de supprimer un snap-in:

Remove-PSSnapin <nom de l'add-in>

Cet article a détaillé les éléments principaux permettent d’utiliser les cmdlets. Les articles suivants permettent de rentrer davantage dans les détails des instructions utilisables dans des scripts Powershell.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page

Powershell en 10 min: exécuter Powershell (partie 1)

Avant de commencer à parler des intructions Powershell, cet article a pour but d’expliquer comment exécuter du code Powershell.

Installation de Powershell

Powershell est livré avec toutes les versions récentes de Windows, toutefois les versions différent suivant la version de Windows:

Version de Powershell Date de sortie Disponible par défaut Disponibilités sur d’autres versions
5.1 Janvier 2017 Windows 10 Anniversary Update Windows 7 SP1,
Windows 8.1,
Windows Server 2012, 2012 R2
5.0 Février 2016 Windows 10 Windows 7 SP1,
Windows 8.1,
Windows Server 2012, 2012 R2
4.0 Octobre 2013 Windows 8.1, Windows Server 2012 R2 Windows 7 SP1
Windows Server 2008 R2 SP1, 2012
3.0 Septembre 2012 Windows 8, Windows Server 2012 Windows 7 SP1
Windows Server 2008 SP2, 2008 R2 SP1
2.0 Octobre 2009 Windows 7, Windows 2008 R2 Windows XP SP3,
Windows Server 2003 SP2
Windows Vista SP1, SP2
Windows Server 2008 SP1, 2008 SP2
1.0 Novembre 2006 Windows Server 2008 Windows XP SP2, SP3
Windows Server 2003 SP1, 2003 SP2, 2003 R2
Windows Vista, Windows Vista SP2

Il est possible d’upgrader la version de Powershell quelque soit la version de Windows citée ci-dessus. Actuellement (août 2017), la version la plus récente est Powershell 5.1 disponible en téléchargeant Windows Management Framework 5.1.

Les versions de Powershell sont rétrocompatibles donc il n’y a pas de risques à installer les dernières versions.

Pour vérifier la version de Powershell, il faut ouvrir une console Powershell et taper:

$PSVersionTable.PSVersion

Le résultat sera du type:

D’une façon générale, plus la version de Powershell est récente et plus elle supporte de commandes. On peut avoir une liste des fonctionnalités disponibles sur: What’s New in Windows PowerShell

Attention aux différences entre Powershell 32 bits et 64 bits

Sur une machine 64 bits, 2 versions de Powershell sont disponibles:

  • 64 bits: %windir%\System32\WindowsPowerShell\v1.0\powershell.exe
  • 32 bits: %windir%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe

Il faut être vigilant sur le choix de la version utilisée en particulier lorsqu’on utilise des snap-ins qui sont compilés pour une architecture précise.

A l’exécution d’un script, on peut connaître l’architecture d’exécution en utilisant la propriété suivante:

[Environment]::Is64BitProcess

Si la valeur est à True alors le script est exécuté dans un environnement 64 bits. Si la valeur est False, l’environnement est 32 bits.

Utilisation de la console

Pour lancer une console Powershell, on peut taper:

  1. Touches [Windows] + [R]
  2. Taper powershell puis [entrée]

On peut aussi taper sur la touche [Windows] puis taper powershell, toutefois il faut faire attention à la version choisie:

  • “Windows Powershell” correspond à la version 64 bits
  • “Windows Powershell (x86)” correspond à la version 32 bits

Enfin à partir du menu Windows, on peut lancer la console en cliquant sur Accessoires ➔ Windows Powershell

Dans la console, quelques fonctionnalités peuvent s’avérer utiles comme:

  • La complétion avec la touche [Tabulation] ou [Majuscule] + [Tabulation]
  • Un historique disponible en tapant [F7] et en choississant avec les flèches la ligne d’historique que l’on souhaite atteidre
  • En cliquant sur le bord en haut à gauche de l’invite de commande Powershell, on peut accéder à d’autres options.

Exécuter un script Powershell

Pour exécuter des scripts Powershell (i.e. fichier ps1) sur une machine, il faut que la politique d’exécution du système le permette. Cette politique n’est pas vraiment un élément de sécurité mais juste un dispositif pour éviter d’exécuter du code Powershell par inadvertance.

Pour connaître la politique d’exécution des scripts powershell sur une machine, il faut exécuter:

Get-ExecutionPolicy -List

Pour avoir plus d’informations sur cette cmdlet, se reporter à Get-ExecutionPolicy.

En exécutant la commande suivante:

Get-ExecutionPolicy -List | Format-Table -AutoSize

On obtient un résultat du type:

Les informations indiquées permettent de savoir quelle est la politique d’exécution suivant une portée (i.e. scope):

  • Process: portée du processus Powershell
  • CurrentUser: portée de l’utilisateur actuel
  • LocalMachine: portée pour tous les utilisateurs de la machine
  • UserPolicy: portée du groupe d’utilisateurs pour l’utilisateur actuel
  • MachinePolicy: portée du groupe d’utilisateurs pour tous les utilisateurs de la machine.

Les valeurs possibles de la politique sont:

  • Restricted: on ne peut pas exécuter des scripts Powershell
  • AllSigned: pour s’exécuter les scripts doivent être signés par un éditeur de confiance.
  • RemoteSigned: les scripts écrits localement sont autorisés en revanche les scripts téléchargés doivent être signés par un éditeur de confiance. Il est possible d’autoriser les scripts téléchargés et non signés en utilisant la cmdlet Unblock-File.
  • Unrestricted: tous les scripts peuvent être exécutés. Un message d’avertissement est affiché lors de l’exécution de scripts téléchargés informant des risques.
  • ByPass: aucune vérification n’est effectuée, tous les scripts peuvent être exécutés.

On peut avoir plus d’informations sur cette politique sur: About Execution Policies.

Dans le cas où on ne peut pas exécuter un script Powershell, à l’exécution on aura une erreur du type:

powershellScript.ps1
powershellScript.ps1 cannot be loaded because the execution of scripts is disabled on this system.

Pour modifier la politique d’exécution et autoriser l’exécution des scripts, on peut exécuter avec les droits administrateur:

Set-ExecutionPolicy RemoteSigned

Pour ouvrir la ligne de commandes Powershell avec les droits administrateur, il faut:

  1. Aller dans Accessoires ➔ Windows PowerShell
  2. Clique droit sur “Windows PowerShell”
  3. Cliquer sur “Exécuter en tant qu’administrateur…”

Comme indiqué plus haut, ce paramètre permettra d’exécuter les scripts locaux sans entrave. En revanche les scripts téléchargés doivent être signés par un éditeur de confiance.
D’autres paramétrages sont plus ouverts comme ByPass ou Unrestricted.

Cette politique n’est pas un dispositif de sécurité mais juste un mécanisme pour éviter d’exécuter des scripts par inadvertance. Même dans le cas où l’exécution n’est pas autorisée, on peut exécuter des cmdlets directement à la ligne de commandes. D’autre part, on peut aussi exécuter des scripts en les affichant directement sur le ligne de commandes et en exécutant avec un pipeline, par exemple:

Get-Content <chemin du script ps1> | PowerShell.exe -noprofile - 

De nombreuses autres méthodes existent: 15 Ways to Bypass the PowerShell Execution Policy.
On peut signer les scripts Powershell: Signing PowerShell Scripts.

Utiliser un environnement pour développer des scripts Powershell

Pour faciliter le développement de scripts Powershell, on peut utiliser le “Windows Powershell ISE” (ISE pour “Integrated Scripting Environment”). Cet environnement est disponible à partir du menu Windows dans Accessoires ➔ Windows PowerShell ➔ Windows PowerShell ISE.

De même que pour l’invite de commande, il existe 2 versions de Windows Powershell ISE:

  • Une version 32 bits identifiée par “Windows Powershell ISE (x86)”
  • Une version 64 bits identifiée par “Windows Powershell ISE”.

Dans l’ISE, on peut avoir des scripts qu’on exécute entiérement ou en partie. Pour exécuter un script entièrement, il suffit d’appuyer sur [F5]. Pour exécuter seulement les commandes surlignées, il faut appuyer sur [F8].

L’ISE se présente de cette façon:

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInEmail this to someonePrint this page