如果出现错误,如何继续处理项目?

时间:2015-10-09 14:56:01

标签: powershell error-handling pipeline

不得不用实际代码重写这篇文章,因为二进制模块肯定存在某种差异。

完整的cmdlet如下(道歉长度):

简而言之,此函数基本上使用SMO库并从SQL Server实例中提取有关数据库用户的信息。

namespace Namespace
{
    using Microsoft.SqlServer.Management.Smo;
    using System;
    using System.Collections.Generic;
    using System.Management.Automation;

    using static PrivateFunctions;

    [Cmdlet(VerbsCommon.Get, "DatabaseUserInformation")]
    public class GetDatabaseUserInformationCmdlet : PSCmdlet
    {
        [Parameter(Mandatory = true,
                   HelpMessage = "The user name of the account to retrieve information for.",
                   Position = 0,
                   ValueFromPipeline = true,
                   ValueFromPipelineByPropertyName = true)]
        public string LogonName { get; set; }

        private List<object> userInformation = new List<object>();

        protected override void ProcessRecord()
        {
            string samAccountName = GetSamAccountName(LogonName);
            Login login = null;
            User user = null;

            WriteVerbose($"Getting login for account: {samAccountName}...");
            try
            {
                login = GetLogin(samAccountName);
            }
            catch (InvalidOperationException invalidOperation)
            {
                ThrowTerminatingError(new ErrorRecord(
                                      invalidOperation,
                                      "LoginNotFound",
                                      ErrorCategory.InvalidOperation,
                                      login));

            }

            WriteVerbose($"Getting user for login: {login.Name}...");
            try
            {
                user = GetUser(login);
            }
            catch (InvalidOperationException invalidOperation)
            {
                ThrowTerminatingError(new ErrorRecord(
                                      invalidOperation,
                                      "UserNotFound",
                                      ErrorCategory.InvalidOperation,
                                      user));
            }

            WriteVerbose($"Gathering information for user: {user.Name}");
            var information = new
            {
                LoginName = login.Name,
                UserName = user.Name,
                FullAccess = TestFullAccessOnDatabase(user),
                Roles = user.EnumRoles()
            };

            userInformation.Add(information);

        }

        protected override void EndProcessing()
        {
            WriteVerbose("Writing information to output.");
            userInformation.ForEach(item => WriteObject(item));
        }
    }
}

cmdlet可以与单个参数一起使用:

Get-DatabaseUserInformation user1

或者我也想在处理多个用户时能够将数组管道化。

@('user1', 'user2','user3') | Get-DatabaseUserInformation

如果我使用的是单个值并且该用户不存在,那么公平,它会终止,并且需要更正并重新运行它。

但是当我将它与多个值一起使用时,如果其中一个值不存在,它就不会提供任何输出,只有例外。

所以当一切正常时我得到的输出是这样的(详细说明):

VERBOSE: Getting login for account: DOMAIN\user1...
VERBOSE: Getting user for login: DOMAIN\user1...
VERBOSE: Gathering information for user: dbo
VERBOSE: Getting login for account: DOMAIN\user2...
VERBOSE: Getting user for login: DOMAIN\user2...
VERBOSE: Gathering information for user: user2
VERBOSE: Getting login for account: DOMAIN\user3...
VERBOSE: Getting user for login: DOMAIN\user3...
VERBOSE: Gathering information for user: user3
VERBOSE: Writing information to output.

LoginName           UserName                     FullAccess Roles
---------           --------                     ---------- -----
DOMAIN\user1        dbo                          True       {db_owner}
DOMAIN\user2        user2                        False      {role1}
DOMAIN\user3        user3                        False      {}

当出现问题时我得到的结果:(在这种情况下,user2拼错了。)

Get-DatabaseUserInformation : A login for the account 'DOMAIN\usr2' does not
exist in the database.
At line:1 char:6
+ $x | Get-DatabaseUserInformation -verbose
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-DatabaseUserInformation], InvalidOperationException
    + FullyQualifiedErrorId : LoginNotFound,DatabaseUserManagement.GetDatabase
   UserInformationCmdlet

我想要发生的事情与此类似。

VERBOSE: Getting login for account: DOMAIN\user1...
VERBOSE: Getting user for login: DOMAIN\user1...
VERBOSE: Gathering information for user: dbo
VERBOSE: Getting login for account: DOMAIN\usr2...
Get-DatabaseUserInformation : A login for the account 'DOMAIN\usr2' does not
exist in the database.
At line:1 char:6
+ $x | Get-DatabaseUserInformation -verbose
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [Get-DatabaseUserInformation], InvalidOperationException
    + FullyQualifiedErrorId : LoginNotFound,DatabaseUserManagement.GetDatabase
   UserInformationCmdlet
VERBOSE: Getting login for account: DOMAIN\user3...
VERBOSE: Getting user for login: DOMAIN\user3...
VERBOSE: Gathering information for user: user3
VERBOSE: Writing information to output.

LoginName           UserName                     FullAccess Roles
---------           --------                     ---------- -----
DOMAIN\user1        dbo                          True       {db_owner}
DOMAIN\user3        user3                        False      {}

4 个答案:

答案 0 :(得分:3)

编辑:问题显然在ThrowTerminating错误中。在这里阅读msdn描述:

https://msdn.microsoft.com/en-us/library/system.management.automation.cmdlet.throwterminatingerror(v=vs.85).aspx

备注部分声明如果要继续处理管道,则不应使用此方法:

  

当cmdlet遇到终止错误时,请调用此方法   而不仅仅是抛出一个例外。调用此方法允许   用于附加描述的其他错误记录信息的cmdlet   导致终止错误的条件。当这个方法是   调用,Windows PowerShell运行时捕获错误记录和   然后开始关闭管道。有关的更多信息   错误报告和错误记录,请参阅Windows PowerShell错误   报告。当错误发生时,不应使用此方法   cmdlet可以继续处理记录。发送错误报告时   发生非终止错误,调用WriteError方法。更多   有关cmdlet的信息,请参阅Windows PowerShell Cmdlet。

你必须写一个像这样的高级函数:

Function Get-StringLength()
{
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline=$true)]
        $value
    )
    Begin {}
    Process {
        Write-Output $value.Length
    }
    End {}
}

Begin块是您准备某些东西的地方,Process块是您从管道处理每个对象的地方,这意味着它是为管道中的每个对象运行的。你清理End块中的混乱。

因为它是一个cmdlet,所以你可以使用ErrorAction来控制错误发生的事情:

$values | Get-StringLength -ErrorAction SilentlyContinue

您甚至可以使用以下方法将这些错误放入变量中:

$values | Get-StringLength -ErrorAction SilentlyContinue -ErrorVariable MyErrors

答案 1 :(得分:1)

答案 2 :(得分:1)

借用早期版本中的代码我创建了一个接受管道输入的简单函数。对于传递的每个对象,我们尝试从Active Directory获取用户。在内部cmdlet上设置-ErrorAction时,我感到很惊讶。所以简单的解决方案是设置一个try / catch块而不是catch动作。

Function Get-AdUsers()
{
    [CmdletBinding()]
    Param(
        [Parameter(ValueFromPipeline=$true)]
        $value
    )
    Process {
        try{Get-ADUser $value}catch{}
    }

}

$values = 'gooduser1','badname','gooduser2'
$values | Get-AdUsers

所以上面返回2个用户对象。如果我删除try块我仍然得到相同的两个对象,但我也得到一个错误。使用try抑制它。

在您的更新代码中,您将收到终止错误,因为这是您定义它的方式。尝试将catch块留空,看看是否有效。

答案 3 :(得分:0)

您需要编写过滤功能,而不是常规功能。为此,将函数体放在process块中:

function Get-StringLength()
{
    [CmdletBinding()]
    Param
    (
        [Parameter(ValueFromPipeline=$true)]
        $value
    )

    process
    {
        Write-Output $value.Length
    }
}

这将导致为管道中的每个项调用一次过滤器。