Adam l’automate
Lorsque vous commencerez à écrire des scripts PowerShell, vous arriverez inévitablement à un endroit où vous devrez traiter plusieurs éléments d’une collection. C’est alors que vous devrez creuser dans les boucles foreach de PowerShell et apprendre ce qu’elles sont.
Presque tous les langages de programmation ont une construction appelée boucles ; PowerShell n’est pas différent. L’un des types de boucles les plus populaires dans PowerShell est la boucle foreach. À sa base, une boucle foreach lit une collection entière d’éléments et foreach item, exécute une sorte de code.
L’un des aspects les plus déroutants de la boucle foreach de PowerShell pour les débutants est toutes les options dont vous disposez. Il n’y a pas qu’une seule façon de traiter chaque élément d’une collection ; il y en a trois !
Dans cet article, vous allez apprendre comment fonctionne chaque type de boucle foreach et quand utiliser l’un plutôt que l’autre. Lorsque vous en aurez terminé avec cet article, vous aurez une bonne compréhension de chaque type de boucle foreach.
Table des matières
Bases de la boucle ForEach de PowerShell
L’un des types de boucles les plus courants que vous utiliserez dans PowerShell est le type de boucle foreach
. Une boucle foreach
lit un ensemble d’objets (itère) et se termine lorsqu’elle a terminé avec le dernier. La collection d’objets lus est généralement représentée par un tableau ou une table de hachage.
NOTE : Le terme itération est un terme de programmation qui fait référence à chaque exécution d’une boucle. Chaque fois qu’une boucle complète un cycle, cela s’appelle une itération. Le fait d’exécuter une boucle sur un ensemble d’objets est communément appelé itération sur l’ensemble.
Parfois, vous devez créer un fichier texte à travers certains dossiers répartis sur un système de fichiers. Disons que les chemins des dossiers sont C:\Folder
C:\Program Files\Folder2
et C:\Folder3
. Sans boucle, nous devrions référencer la cmdlet Add-Content
trois fois.
Add-Content -Path 'C:\Folder\textfile.txt' -Value 'This is the content of the file'Add-Content -Path 'C:\Program Files\Folder2\textfile.txt' -Value 'This is the content of the file'Add-Content -Path 'C:\Folder2\textfile.txt' -Value 'This is the content of the file'
Quelle est la seule différence entre chacune de ces références de commande ? Il s’agit de la valeur Path
. La valeur de Path
est la seule valeur qui change parmi chacune d’elles.
Vous dupliquez beaucoup de code. Vous perdez du temps à taper et vous vous ouvrez à des problèmes sur la route. Au lieu de cela, vous devriez créer un « ensemble » qui inclut juste tous les éléments qui changent. Pour cet exemple, nous allons utiliser un tableau.
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
Vous avez maintenant chacun de ces chemins stockés dans un seul « ensemble » ou tableau. Vous êtes maintenant prêt à utiliser une boucle pour itérer sur chacun de ces éléments. Avant de le faire, cependant, c’est le bon moment pour mentionner un sujet qui fait généralement trébucher les nouveaux venus dans PowerShell. Contrairement aux autres types de boucles, les boucles foreach
ne sont pas une seule et même chose.
Il existe techniquement trois types de boucles foreach
dans PowerShell. Bien que chacune soit similaire à utiliser, il est important de comprendre la différence.
L’instruction foreach
Le premier type de boucle foreach
est une instruction. foreach
est un mot-clé interne à PowerShell qui n’est ni un cmdlet ni une fonction. L’instruction foreach
est toujours utilisée dans le formulaire : foreach ($i in $array)
.
En utilisant l’exemple ci-dessus, la variable $i
représente l’itérateur ou la valeur de chaque élément dans $path
car elle itère sur chaque élément du tableau.
Notez que la variable iterator n’a pas besoin d’être
$i
. Le nom de la variable peut être n’importe quoi.
Dans l’exemple ci-dessous, vous pouvez accomplir la même tâche que la répétition de la référence Add-Content
en faisant ceci :
# Create an array of folders$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')# Perform iteration to create the same file in each folderforeach ($i in $folders) { Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"}
L’instruction foreach
est connue pour être une alternative plus rapide que l’utilisation du cmdlet ForEach-Object
.
Le cmdlet ForEach-Object
Si foreach
est une déclaration et ne peut être utilisée que d’une seule manière, ForEach-Object
est un cmdlet avec des paramètres qui peuvent être employés de nombreuses manières différentes. Comme l’instruction foreach
, la cmdlet ForEach-Object
peut itérer sur un ensemble d’objets. Seulement cette fois, il passe cet ensemble d’objets et l’action à effectuer sur chaque objet en tant que paramètre, comme indiqué ci-dessous.
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')$folders | ForEach-Object (Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file")
Note : Pour rendre les choses plus confuses, la
ForEach-Object
cmdlet a un alias appeléforeach
. Selon la façon dont le terme « foreach » est appelé, l’instructionforeach
est exécutée, ou laForEach-Object
s’exécute.Une bonne façon de différencier ces situations est de remarquer si le terme « foreach » est suivi de
($someVariable in $someSet)
. Sinon, dans tout autre contexte, l’auteur du code utilise probablement l’alias deForEach-Object
.
La méthode foreach()
L’une des plus récentes foreach
boucles a été introduite dans PowerShell v4 appelée méthode foreach()
. Cette méthode existe sur un tableau ou un objet de collection. La méthode foreach()
possède un paramètre de bloc de script standard qui contient les actions à entreprendre à chaque itération, tout comme les autres.
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')$folders.ForEach({Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file"})
La différence la plus significative avec la méthode foreach()
est son fonctionnement sous le capot.
L’utilisation de la méthode
foreach()
est considérablement plus rapide et l’est sensiblement sur de grands ensembles. Il est recommandé d’utiliser cette méthode plutôt que les deux autres si possible.
Mini-projet : Boucle sur un ensemble de noms de serveurs
L’une des utilisations les plus courantes d’une boucle dans PowerShell consiste à lire un ensemble de serveurs à partir d’une source quelconque et à effectuer une action sur chacun d’eux. Pour notre premier mini-projet, construisons un code qui nous permettra de lire des noms de serveurs (un par ligne) à partir d’un fichier texte et d’effectuer un ping sur chaque serveur pour déterminer s’il est en ligne ou non.
Pour ce flux de travail, quel type de boucle pensez-vous fonctionnerait le mieux ?
Vous avez remarqué que j’ai mentionné le mot » set » comme dans » set of servers « . C’est un indice juste là. Vous avez déjà un nombre spécifié de noms de serveurs, et vous aimeriez effectuer une action sur chacun d’eux. Cela semble être une excellente occasion d’essayer la boucle PowerShell la plus couramment utilisée ; la boucle foreach
.
La première tâche consiste à créer un ensemble de noms de serveurs dans le code. La façon la plus courante de le faire est de créer un tableau. Heureusement pour nous que Get-Content
, par défaut, renvoie un tableau dont chaque élément est représenté par une seule ligne dans le fichier texte.
Premièrement, créez un tableau de tous mes noms de serveurs et appelez-le $servers
.
$servers = Get-Content .\servers.txt
Maintenant que vous avez créé le tableau, vous allez devoir confirmer si chaque serveur est en ligne ou non. Un excellent cmdlet pour tester la connexion d’un serveur s’appelle Test-Connection
. Cette cmdlet effectue quelques tests de connexion sur un ordinateur pour voir s’il est en ligne ou non.
En exécutant Test-Connection
à l’intérieur d’une boucle foreach
pour lire chaque ligne de fichier, vous pouvez passer chaque nom de serveur représenté par la variable $server
Test-Connection
. Voilà comment PowerShell peut boucler un fichier texte. Vous pouvez voir un exemple de ce fonctionnement ci-dessous.
foreach ($server in $servers) {try {$null = Test-Connection -ComputerName $server -Count 1 -ErrorAction STOPWrite-Output "$server - OK"}catch {Write-Output "$server - $($_.Exception.Message)"}}
Lorsque la boucle foreach
est exécutée, elle ressemblera alors à quelque chose comme ceci :
.Connexion à travers une liste de noms de serveurs
Vous avez maintenant testé avec succès la connexion d’un fichier texte rempli de noms de serveurs ! À ce stade, vous pouvez maintenant ajouter ou supprimer des noms de serveurs à l’intérieur du fichier texte à volonté sans modifier le code.
Vous avez pratiqué avec succès ce que nous avons prêché, la méthode DRY.
ForEach Exemples PowerShell
Regardons quelques exemples d’utilisation de la boucle ForEach. Ceux-ci sont basés sur des cas d’utilisation réels montrant le concept que vous pouvez modifier pour l’adapter à vos besoins.
Exemple 1 : Création d’un fichier dans chaque sous-dossier d’un répertoire à l’aide de l’instruction ForEach
Cet exemple démontre l’utilisation courante de PowerShell foreach folder dans un répertoire.
Supposons qu’il existe dix sous-dossiers à l’intérieur du dossier C:\ARCHIVE_VOLUMES
. Chaque sous-dossier représente un volume d’archives qui est sauvegardé quotidiennement. Une fois chaque sauvegarde terminée, un fichier nommé BackupState.txt
est créé à l’intérieur de chaque dossier contenant la date à laquelle il a été sauvegardé.
Vous pouvez voir un exemple de ce à quoi cela pourrait ressembler ci-dessous.
Le script ci-dessous effectue trois actions :
- obtient la liste de tous les sous-dossiers à l’intérieur de
C:\ARCHIVE_VOLUMES
- créent un fichier texte nommé
BackupState.txt
contenant la date et l’heure actuelles sa valeur
Les boucles à travers chaque dossier
L’exemple ci-dessous utilise l’instruction foreach
.
# Define the TOP-level folder$TOP_FOLDER = "C:\ARCHIVE_VOLUMES"# Get all sub folders recursively$Child_Folders = Get-ChildItem -Path $TOP_FOLDER -Recurse | Where-Object { $_.PSIsContainer -eq $true }# Create a text file in each sub-folder and add the current date/time as value.foreach ($foldername in $Child_Folders.FullName) { (get-date -Format G) | Out-File -FilePath "$($foldername)\BackupState.txt" -Force}
En utilisant la cmdlet Get-ChildItem
, vous pouvez confirmer que les fichiers ont été créés ou mis à jour à l’intérieur de chacun des sous-dossiers.
Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include backupstate.txt | Select-Object Fullname,CreationTime,LastWriteTime,Length
La capture d’écran ci-dessous montre la sortie du script affichant tous les BackupState.txt
fichiers trouvés dans chaque sous-répertoire.
Exemple 2 : Lecture du contenu de chaque fichier texte dans les sous-répertoires
Puis, pour démontrer l’utilisation du fichier foreach de PowerShell dans un répertoire, le script ci-dessous va lire chaque BackupState.txt
fichier créé dans l’exemple 1.
- Trouver récursivement tous les
BackupState.txt
fichiers à l’intérieur de chaque sous-répertoire. - Utilisant l’instruction
foreach
, lisez chaque fichier texte pour obtenir la valeur « last backup time ». - Affichez le résultat à l’écran.
## Find all BackupState.txt files in C:\ARCHIVE_VOLUMES$files = Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include 'BackupState.txt' | Select-Object DirectoryName,FullName## Read the contents of each fileforeach ($file in $files) { Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))}
Une fois le script exécuté dans votre session PowerShell, vous devriez voir une sortie similaire à la capture d’écran ci-dessous. Cela montre que PowerShell boucle dans les fichiers, lit le contenu et affiche la sortie.
Exemple 3 : Obtenir des services et les démarrer à l’aide du CmdLet ForEach-Object
Les administrateurs système ont souvent besoin d’obtenir l’état des services et de déclencher un workflow manuel ou automatique pour remédier à tout service défaillant. Examinons l’exemple de script utilisant le ForEach-Object
cmdlet.
Pour cet exemple, le script ci-dessous fera ce qui suit :
- Obtenir une liste des services qui sont configurés pour un démarrage automatique mais qui ne sont actuellement pas dans un état d’exécution.
- Puis, les éléments de la liste sont acheminés vers le cmdlet
ForEach-Object
pour tenter de démarrer chaque service. - Un message de réussite ou d’échec s’affiche en fonction du résultat de la
Start-Service
commande.
## Get a list of automatic services that are stopped.$services = Get-Service | Where-Object {$.StartType -eq 'Automatic' -and $.Status -ne 'Running'}## Pass each service object to the pipeline and process them with the Foreach-Object cmdlet$services | ForEach-Object { try { Write-Host "Attempting to start '$($.DisplayName)'" Start-Service -Name $.Name -ErrorAction STOP Write-Host "SUCCESS: '$($.DisplayName)' has been started" } catch { Write-output "FAILED: $($.exception.message)" }}
Lorsque le script est exécuté, il ressemble à la capture d’écran de sortie ci-dessous. Comme vous pouvez le voir, chaque service a reçu une commande de démarrage. Certains ont été lancés avec succès tandis que d’autres n’ont pas réussi à démarrer.
Exemple 4 : Lecture des données d’un CSV à l’aide de la méthode ForEach()
L’utilisation des données des fichiers CSV est populaire parmi les administrateurs système. Mettre des enregistrements à l’intérieur d’un fichier CSV facilite l’exécution d’opérations en masse en utilisant la combinaison Import-CSV
et ForEach
. Cette combinaison est couramment utilisée pour créer plusieurs utilisateurs dans Active Directory.
Dans cet exemple suivant, on suppose que vous disposez d’un fichier CSV avec deux colonnes – Prénom et Nom. Ce fichier CSV doit ensuite être alimenté avec les noms des nouveaux utilisateurs à créer. Le fichier CSV ressemblerait à quelque chose comme ci-dessous.
"Firstname","Lastname""Grimm","Reaper""Hell","Boy""Rick","Rude"
Maintenant pour le bloc de script suivant. Tout d’abord, importez le fichier CSV en passant le chemin du contenu à la cmdlet Import-CSV
. Ensuite, en utilisant la méthode foreach()
, bouclez à travers les noms et créez les nouveaux utilisateurs dans Active Directory.
# Import list of Firstname and Lastname from CSV file$newUsers = Import-Csv -Path .\Employees.csvAdd-Type -AssemblyName System.Web# Process the list$newUsers.foreach( { # Generate a random password $password = ::GeneratePassword((Get-Random -Minimum 20 -Maximum 32), 3) $secPw = ConvertTo-SecureString -String $password -AsPlainText -Force # Formulate a username $userName = '{0}{1}' -f $_.FirstName.Substring(0, 1), $_.LastName # Build new user attributes $NewUserParameters = @{ GivenName = $_.FirstName Surname = $_.LastName Name = $userName AccountPassword = $secPw } try { New-AdUser @NewUserParameters -ErrorAction Stop Write-Output "User '$($userName)' has been created." } catch { Write-Output $_.Exception.Message } })
Lors de l’exécution, vous devriez maintenant avoir créé un utilisateur AD pour chaque ligne du fichier CSV !
Résumé
La logique derrière la boucle foreach dans PowerShell n’est pas différente de celle des autres langages de programmation. Elle change seulement en fonction de la façon dont elle est utilisée et de la variante de la boucle foreach choisie pour une tâche spécifique.
Dans cet article, vous avez appris les différents types de boucles foreach disponibles dans PowerShell, et ce qu’il faut prendre en compte pour choisir celle à utiliser. Vous avez également vu les trois types de boucles foreach en action à l’aide de différents exemples de scénarios.
Lecture complémentaire
- Gestion des fichiers CSV dans PowerShell avec Import-Csv (boucle foreach)
- New-ADUser : créer des utilisateurs Active Directory avec PowerShell
- Bases de la boucle PowerShell : Foreach | Scripting Blog
.