PowerShell 有個 -ErrorAction SilentlyContinue 參數可在出錯時隱藏錯誤訊息,並繼續執行命令。

今天發現,ErrorAction 參數不是對所有命令都有效,以下是個簡單測試,用 Get-LocalGroup 跟 Get-ADGroup 查詢本機及 AD 群組,故意輸入不存在的群組名稱會出現錯誤訊息。Get-LocalGroup 可加上 -ErrorAction SilentlyContinue 不顯示錯誤,但 Get-ADGroup 加了卻沒效。

查了文件發現,PowerShell 命令有兩種拋出錯誤的方式,Cmdlet.ThrowTerminatingError(ErrorRecord)ICommandRuntime.WriteError(ErrorRecord),ErrorAction 參數只對後者有效,前者會中斷程式,但可以用 Try Catch 捕捉例外,所以真要出錯時繼續,用 Try Catch 包起來就對了。

Try {
    $Grp = Get-ADGroup 'NoSuchGroup'
} 
Catch {
    # NOT FOUND OR OTHER ERROR
}

好奇心起,追了一下原始碼找到 Get-ADGroup 是用 ThrowTerminatingError 的證據:

Microsoft.ActiveDirectory.Management.Commands.ADCmdletBase 型別

internal virtual void ProcessError(Exception e)
{
    if (e is RuntimeException)
    {
        throw e;
    }
    bool flag = true;
    this._recordExceptionHandler.ProcessingRecord = this._processingRecord;
    if (this != null && ((IADCustomExceptionFiltering)this).ExceptionFilter.FilterException(e, ref flag) && !flag)
    {
        this.WriteErrorBuffered(this.ConstructErrorRecord(e));
        return;
    }
    base.ThrowTerminatingError(this.ConstructErrorRecord(e));
}

Micrsoft.PowerShell.LocalAccounts.GetLocalGroupCommand 則是用 WriteError:

private void ProcessNames()
{
    if (this.Name != null)
    {
        foreach (string text in this.Name)
        {
            try
            {
                if (WildcardPattern.ContainsWildcardCharacters(text))
                {
                    WildcardPattern pattern = new WildcardPattern(text, WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
                    Sam sam = this.sam;
                    Predicate<string> pred;
                    Predicate<string> <>9__0;
                    if ((pred = <>9__0) == null)
                    {
                        pred = (<>9__0 = ((string n) => pattern.IsMatch(n)));
                    }
                    using (IEnumerator<LocalGroup> enumerator = sam.GetMatchingLocalGroups(pred).GetEnumerator())
                    {
                        while (enumerator.MoveNext())
                        {
                            LocalGroup sendToPipeline = enumerator.Current;
                            base.WriteObject(sendToPipeline);
                        }
                        goto IL_A7;
                    }
                }
                base.WriteObject(this.sam.GetLocalGroup(text));
                IL_A7:;
            }
            catch (Exception ex)
            {
                base.WriteError(ex.MakeErrorRecord(null));
            }
        }
    }
}

打完收工。

The reason why ErrorAction SilentlyContinue doesn't work on some cmdlets is because it depends on whether the cmdlets use WriteError or ThrowTerminatingError. For the latter, you need to use a try-catch block.


Comments

Be the first to post a comment

Post a comment