当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 回归基础:PowerShell Foreach 循环

作者:精品下载站 日期:2024-12-14 20:53:36 浏览:13 分类:玩电脑

回归基础:PowerShell Foreach 循环


当您第一次开始编写 PowerShell 脚本时,您将不可避免地遇到需要处理集合中的多个项目的情况。然后您将需要深入研究 PowerShell foreach 循环并了解它们的全部内容。

几乎所有的编程语言都有一个称为循环的结构。 PowerShell 也不例外。 PowerShell 中最流行的循环类型之一是 foreach 循环。最基本的是,foreach 循环读取整个项目集合,foreach 项目运行某种代码。

对于初学者来说,PowerShell foreach 循环最令人困惑的方面之一是您拥有的所有选项。处理集合中的每一项的方法不只有一种;有三种!

在本文中,您将了解每种类型的 foreach 循环的工作原理以及何时使用其中一种循环。当您读完本文时,您将对每种类型的 foreach 循环有很好的了解。

PowerShell ForEach 循环基础知识

在 PowerShell 中最常见的循环类型之一是 foreach 类型的循环。 foreach 循环读取一组对象(迭代),并在完成最后一个对象后完成。读取的对象集合通常由数组或哈希表表示。

注意:术语“迭代”是一个编程术语,指的是循环的每次运行。每次循环完成一个循环,这称为一次迭代。在一组对象上运行循环的行为通常称为迭代该组。

也许您需要在分布在文件系统中的一些文件夹中创建一个文本文件。假设文件夹路径为 C:\FolderC:\Program Files\Folder2C:\Folder3。如果没有循环,我们就必须引用 Add-Content cmdlet 三次。

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'

这些命令参考之间的唯一区别是什么?它是Path 值。 Path 的值是其中每个值中唯一发生变化的值。

你复制了很多代码。你浪费时间打字,并为以后的问题敞开心扉。相反,您应该创建一个仅包含所有正在更改的项目的“集”。对于这个例子,我们将使用一个数组。

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

现在,您已将每个路径存储在单个“集”或数组中。您现在准备好使用循环来迭代这些元素中的每一个。不过,在执行此操作之前,最好先提一下通常会让 PowerShell 新手困惑的主题。与其他类型的循环不同,foreach 循环不同。

从技术上讲,PowerShell 中存在三种类型的 foreach 循环。尽管每种方法的用途都很相似,但理解其中的差异很重要。

foreach 语句

第一种类型的 foreach 循环是一个语句foreach 是一个内部 PowerShell 关键字,它既不是 cmdlet,也不是函数。 foreach 语句始终以以下形式使用:foreach ($i in $array)

使用上面的示例,$i 变量表示迭代器$path 中每个项目的值,因为它迭代 数组中的每个元素。

请注意,迭代器变量不必是 $i。变量名称可以是任何内容。

在下面的示例中,您可以通过执行以下操作来完成与重复 Add-Content 引用相同的任务:

# Create an array of folders
$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')

# Perform iteration to create the same file in each folder
foreach ($i in $folders) {
    Add-Content -Path "$i\SampleFile.txt" -Value "This is the content of the file"
}

众所周知,foreach 语句比使用 ForEach-Objectcmdlet 更快。

ForEach-Object CmdLet

如果 foreach 是一个语句并且只能以单一方式使用,那么 ForEach-Object 是一个带有参数的 cmdlet,可以以多种不同的方式使用。与foreach 语句类似,ForEach-Object cmdlet 可以迭代一组对象。只是这一次,它传递了该组对象以及对每个对象采取的操作作为参数,如下所示。

$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")

注意:为了让事情变得混乱,ForEach-Object cmdlet 有一个名为 foreach 的别名。根据术语“foreach”的调用方式,运行 foreach 语句,或运行 ForEach-Object

区分这些情况的一个好方法是注意术语“foreach”后面是否跟着 ($someVariable in $someSet)。否则,在任何其他上下文中,代码作者可能会使用 ForEach-Object 的别名。

foreach() 方法

PowerShell v4 中引入了最新的 foreach 循环之一,称为 foreach() 方法。此方法存在于数组或集合对象上。 foreach() 方法有一个标准脚本块参数,其中包含接管每次迭代的操作,就像其他迭代一样。

$folders = @('C:\Folder','C:\Program Files\Folder2','C:\Folder3')
$folders.ForEach({
	Add-Content -Path "$_\SampleFile.txt" -Value "This is the content of the file"
})

foreach() 方法最显着的区别在于它在底层的工作方式。

使用 foreach() 方法要快得多,并且在大型集合上尤其如此。如果可能的话,建议使用此方法而不是其他两种方法。

迷你项目:循环一组服务器名称

PowerShell 中循环最常见的用途之一是从某个源读取一组服务器并对每个服务器执行某些操作。对于我们的第一个小型项目,让我们构建一些代码,允许我们从文本文件中读取服务器名称(每行一个)并 ping 每台服务器以确定它们是否在线。

对于此工作流程,您认为哪种类型的循环效果最好?

请注意,我在“服务器集”中提到了“集”一词。这是一条线索。您已经拥有指定数量的服务器名称,并且您希望对其中的每个执行一些操作。这听起来是一个尝试最常用的 PowerShell 循环的好机会; foreach 循环。

第一个任务是在代码中创建一组服务器名称。最常见的方法是创建一个数组。对我们来说幸运的是,默认情况下,Get-Content 返回一个数组,数组中的每个元素都由文本文件中的一行表示。

首先,创建一个包含所有服务器名称的数组,并将其命名为 $servers

$servers = Get-Content .\servers.txt

现在您已经创建了阵列,您现在需要确认每个服务器是否在线。用于测试服务器连接的一个很棒的 cmdlet 称为 Test-Connection。此 cmdlet 在计算机上执行一些连接测试,以查看其是否在线。

通过在 foreach 循环内执行 Test-Connection 来读取每个文件行,您可以将 $server 变量表示的每个服务器名称传递给测试连接。这就是 PowerShell 循环遍历文本文件的方式。您可以在下面查看其工作原理的示例。

foreach ($server in $servers) {
	try {
		$null = Test-Connection -ComputerName $server -Count 1 -ErrorAction STOP
		Write-Output "$server - OK"
	}
	catch {
		Write-Output "$server - $($_.Exception.Message)"
	}
}

当执行 foreach 循环时,它将如下所示:

您现在已经成功测试了充满服务器名称的文本文件的连接!此时,您现在可以在文本文件中随意添加或删除服务器名称,而无需更改代码。

您已经成功实践了我们一直宣扬的 DRY 方法。

ForEach PowerShell 示例

让我们看几个如何使用 ForEach 循环的示例。这些基于现实世界的用例,显示了您可以修改以满足您的要求的概念。

示例 1:使用 ForEach 语句在目录中的每个子文件夹中创建文件

此示例演示了 PowerShell foreach 目录中文件夹的常见用法。

假设 C:\ARCHIVE_VOLUMES 文件夹中有十个子文件夹。每个子文件夹代表一个每天备份的存档卷。每次备份完成后,都会在每个文件夹内创建一个名为 BackupState.txt 的文件,其中包含备份日期。

您可以在下面看到一个示例。

下面的脚本执行三个操作:

  • 获取 C:\ARCHIVE_VOLUMES 内所有子文件夹的列表
  • 循环遍历每个文件夹
  • 创建一个名为 BackupState.txt 的文本文件,其中包含当前日期和时间及其值

下面的示例使用 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
}

使用 Get-ChildItem cmdlet,您可以确认每个子文件夹内的文件已创建或更新。

Get-ChildItem -Recurse -Path C:\ARCHIVE_VOLUMES -Include backupstate.txt | Select-Object Fullname,CreationTime,LastWriteTime,Length

下面的屏幕截图显示了脚本的输出,其中显示了每个子目录中找到的所有 BackupState.txt 文件。

示例2:读取子目录中每个文本文件的内容

接下来,为了演示如何使用 PowerShell foreach 目录中的文件,下面的脚本将读取示例 1 中创建的每个 BackupState.txt 文件。

  • 递归查找每个子目录中的所有 BackupState.txt 文件。
  • 使用 foreach 语句读取每个文本文件以获取“上次备份时间”值。
  • 在屏幕上显示结果。
## 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 file
foreach ($file in $files) {
    Write-Output ("$($file.DirectoryName) last backup time - " + (Get-Content $file.FullName))
}

在 PowerShell 会话中执行脚本后,您应该会看到与下面的屏幕截图类似的输出。这表明 PowerShell 循环文件、读取内容并显示输出。

示例 3:使用 ForEach-Object CmdLet 获取服务并启动它们

系统管理员通常需要获取服务的状态并触发手动或自动工作流程来修复任何失败的服务。让我们看一下使用 ForEach-Object cmdlet 的示例脚本。

对于本示例,下面的脚本将执行以下操作:

  • 获取配置为自动启动但当前未处于运行状态的服务的列表。
  • 接下来,列表中的项目通过管道传输到 ForEach-Object cmdlet 以尝试启动每个服务。
  • 根据 Start-Service 命令的结果,显示成功失败消息。
## 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)"
    }
}

执行脚本时,它将类似于下面的输出屏幕截图。如您所见,每个服务都发出了启动命令。有的启动成功,有的启动失败。

示例 4:使用 ForEach() 方法从 CSV 读取数据

使用 CSV 文件中的数据在系统管理员中很流行。使用 Import-CSVForEach 组合将记录放入 CSV 文件中可以轻松运行批量操作。此组合通常用于在 Active Directory 中创建多个用户。

在下一个示例中,假设您有一个包含两列的 CSV 文件 - 名字姓氏。然后应使用新用户的姓名填充该 CSV 文件,以便被创建。 CSV 文件如下所示。

"Firstname","Lastname"
"Grimm","Reaper"
"Hell","Boy"
"Rick","Rude"

现在进入下一个脚本块。首先,通过将内容路径传递给 Import-CSV cmdlet 来导入 CSV 文件。然后,使用 foreach() 方法循环访问名称并在 Active Directory 中创建新用户。

# Import list of Firstname and Lastname from CSV file
$newUsers = Import-Csv -Path .\Employees.csv

Add-Type -AssemblyName System.Web

# Process the list
$newUsers.foreach(
    {
        # Generate a random password
        $password = [System.Web.Security.Membership]::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
        }
    }
)

运行时,您现在应该已经为 CSV 文件中的每一行创建了一个 AD 用户!

概括

PowerShell 中 foreach 循环背后的逻辑与其他编程语言没有什么不同。它仅随着其使用方式以及为任何特定任务选择 foreach 循环的哪种变体而变化。

在本文中,您了解了 PowerShell 中可用的不同类型的 foreach 循环,以及在使用哪种循环时应考虑的因素。您还了解了使用不同示例场景的三种类型的 foreach 循环。

进一步阅读

  • 使用 Import-Csv(foreach 循环)在 PowerShell 中管理 CSV 文件
  • New-ADUser:使用 PowerShell 创建 Active Directory 用户
  • PowerShell 循环基础知识:Foreach |脚本博客

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯