【问题标题】:Edit value in CSV using powershell使用 powershell 在 CSV 中编辑值
【发布时间】:2017-04-24 17:57:31
【问题描述】:

对于一个小型项目,我想从一个小的 CSV 文件创建 AD 用户。我制作了每隔几分钟运行一次的作业(PowerShell 脚本),以根据我在脚本中定义的用户状态来创建用户。例如;在 AD 中创建用户时,我将状态更改为 addmailbox。然后另一个工作会来添加邮箱。

一切都很顺利,但我发现了一个错误。我在 CSV 文件中编辑状态的方式是错误的,因为当我只想更改一个时,我更改了 CSV 中所有用户的状态。但我似乎无法让任何其他方法起作用。

#When encountering any error, stop the script.
$ErrorActionPreference = "Stop";

#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv

if($FileExists -eq $true) {
    $Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
    foreach ($user in $Users) {
        $Status = $User.Status;
        $userPrincipalName = $User.userPrincipalName;
        $SAMAccountName = $User.SAMAccountName;
        $userInstance =  $User.userInstance;
        $Name = $User.Name;
        $displayName = $User.DisplayName;
        $Path = '"' + $User.Path + '"';
        $GivenName = $User.GivenName;
        $Surname = $User.Surname;
        $SIP = $userPrincipalName;

        if ($Status -eq "CreateUser") {
            try {
                #create user
                Import-Module ActiveDirectory; 
                New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;

                #change status
                (Get-Content  C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'}  | Out-File  C:\inetpub\wwwroot\melle\data.csv

                #exit on completion
                Exit(Write-Host 'User was created in AD.');
            } Catch {
                #write any errors to error log.
                $_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
            }
        } else {
            Exit(Write-Host 'No user with status CreateUser was found.');
        }
    }
}else {
    Exit(Write-Host 'No data.csv file was found.');
}

我现在的做法是(Get-Content C:\inetpub\wwwroot\melle\data.csv) | Foreach-Object {$_ -replace 'CreateUser','AddMailbox'} | Out-File C:\inetpub\wwwroot\melle\data.csv,但我只想在foreach 中为脚本正在与之对话的行定义它。

我尝试在此处和其他网站上搜索解决方案,但我无法获得其他人用来处理我的脚本的解决方案。有些脚本太高级了,我无法理解。

在循环中只更改我正在处理的线路的状态的正确方法是什么?

更新:已解决 工作脚本:

#Check if there is a data file.
$FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv

if($FileExists -eq $true) {
$Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"

$UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};

# Check that we have users that need to be created, might need fine-tuning.
if($UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
    # Write-Host doesn't have a meaningful return code, so you can separate those lines.

    Exit(Write-Host 'No user with status CreateUser was found.');
}else{
    # This way it's only run once
    Import-Module ActiveDirectory; 
}

$Users | ForEach-Object {
    if($_.Status -eq 'CreateUser'){
        try {
            #create user
            New-ADUser -SAMAccountName $_.SAMAccountName -Instance "'" + $_.userInstance + "'" -Name $_.name -DisplayName $_.displayName -Path "working.path.here" -GivenName $_.givenname -Surname $_.surname -userPrincipalName $_.userprincipalname -AccountPassword (ConvertTo-SecureString -String "Welcome01" -AsPlainText -Force) -PassThru | Enable-ADAccount;

            #change status
            $_.Status = 'AddMailbox';
        } Catch {
            #write any errors to error log.
            $_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
        }
    }
}

$Users | ConvertTo-Csv | Out-File 'C:\inetpub\wwwroot\melle\data.csv'


Exit(Write-Host 'User was created in AD.');
}else {

Exit(Write-Host 'No data.csv file was found.');
}

【问题讨论】:

    标签: powershell csv


    【解决方案1】:

    您已经注意到,您当前的方法行不通。您正在获取 CSV 的全部内容并替换每个实例。 Import-Csv 语句实际上为您提供了一个允许您更改值的数据结构。你只需要事后写回来。

    这种方法仍然容易出错,并且您会遇到问题。如果我对您的理解正确,您希望跟踪状态并拥有多个执行不同操作的脚本。迟早您会遇到由于竞争访问请求而导致它们相互覆盖更改和/或锁定的情况。如果你想这样做,你应该考虑使用支持基于行的锁定的数据库(很可能这样做)。否则,您将需要找到一种方法让它们按顺序运行或自己实现基于行的锁定。

    也就是说,一种可能的解决方案可能如下所示。我还没有运行它,但基本结构应该是正确的。覆盖更改的一个示例是创建邮箱可能需要很长时间。由于文件仅在脚本开头读取并在结尾写入,因此它可能会在两者之间发生变化。

    #When encountering any error, stop the script.
    $ErrorActionPreference = "Stop";
    
    #Check if there is a data file.
    $FileExists = Test-Path C:\inetpub\wwwroot\melle\data.csv
    
    if($FileExists -eq $true) {
        $Users = Import-Csv -Path "C:\inetpub\wwwroot\melle\data.csv"
    
        $UsersThatNeedToBeCreated = $Users | Where-Object {$_.Status -eq 'CreateUser'};
    
        # Check that we have users that need to be created, might need fine-tuning.
        if($$UsersThatNeedToBeCreated -eq $null -or $UsersThatNeedToBeCreated.Count -eq 0){
            # Write-Host doesn't have a meaningful return code, so you can separate those lines.
            Write-Host 'No user with status CreateUser was found.'
            Exit;
        }else{
            # This way it's only run once
            Import-Module ActiveDirectory; 
        }
    
        $Users | ForEach-Object {
            $Status = $User.Status;
            $userPrincipalName = $User.userPrincipalName;
            $SAMAccountName = $User.SAMAccountName;
            $userInstance =  $User.userInstance;
            $Name = $User.Name;
            $displayName = $User.DisplayName;
            $Path = '"' + $User.Path + '"';
            $GivenName = $User.GivenName;
            $Surname = $User.Surname;
            $SIP = $userPrincipalName;
    
            if($_.Status -eq 'CreateUser'){
                try {
                    #create user
                    New-ADUser -SAMAccountName $SAMAccountName -Instance $userInstance -Name $name -DisplayName $displayName -Path "correct.path.com" -GivenName $givenname -Surname $surname -userPrincipalName $userprincipalname -AccountPassword (ConvertTo-SecureString -String "bla" -AsPlainText -Force) -PassThru | Enable-ADAccount;
    
                    # change status
                    $_.Status = 'AddMailbox';
                } Catch {
                    # write any errors to error log.
                    $_ | Out-File C:\inetpub\wwwroot\melle\errors.log -Append;
                }
            }
        }
    
        $Users | ConvertTo-Csv -NoTypeInformation | Out-File 'C:\inetpub\wwwroot\melle\data.csv'
    
        Write-Host 'User was created in AD.'
        Exit
    }else {
        Write-Host 'No data.csv file was found.'
        Exit
    }
    

    【讨论】:

    • 嗨,Seth,感谢您到目前为止的帮助。我喜欢您所做的更改,但 CSV 中的状态并没有以这种方式更改。它保持不变。此外,您不能使用 Exit();我注意到()内没有任何表达式。 CSV 文件插入了这一行:#TYPE System.Management.Automation.PSCustomObject。并且没有创建用户,但我注意到我在脚本中使用的变量不再存在,所以我将再次定义它们。
    • 抱歉,我只专注于更改状态。您应该能够在没有括号的情况下运行Exit。很难猜测你的实际目标是什么。 Write-Host 会写入一个实际的控制台,并且不能轻易被重定向。此外,通过使用括号调用Exit,您(可能)从Write-Host 返回空结果,这对您没有任何帮助。
    • 你说得对,问题是我不需要捕捉这些错误,我只希望脚本在它们发生时退出。我要记录的唯一错误是命令中是否出现任何问题,该命令正在运行。我做了一些小改动并让它工作。非常感谢!我将使用更新的脚本编辑我的帖子。
    • 此外,正如您在编辑中看到的,您需要传递-NoTypeInformation 才能跳过ConvertTo-Csv 的...类型信息。如果您有多个脚本出于上述原因访问此文件,我仍然不建议使用这种方法。如果它有帮助,可以考虑将其标记为答案,或者如果您愿意,可以投票赞成。
    • 再次感谢,你帮了我很大的忙,因为我被困了一段时间。如果我有足够的代表来投票,我会这样做:)
    猜你喜欢
    • 2014-01-15
    • 2020-08-12
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 2018-07-25
    • 2021-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多