diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-object/Format-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-object/Format-Object.cs
index 2179243279d..c2c16ff2cd1 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-object/Format-Object.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-object/Format-Object.cs
@@ -41,6 +41,12 @@ public object[] Property
private object[] _props;
+ ///
+ /// Gets or sets the properties to exclude from formatting.
+ ///
+ [Parameter]
+ public string[] ExcludeProperty { get; set; }
+
///
///
///
@@ -61,6 +67,18 @@ internal override FormattingCommandLineParameters GetCommandLineParameters()
{
FormattingCommandLineParameters parameters = new();
+ // Check View conflicts first (before any auto-expansion)
+ if (!string.IsNullOrEmpty(this.View))
+ {
+ // View cannot be used with Property or ExcludeProperty
+ if ((_props is not null && _props.Length != 0) || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
+ {
+ ReportCannotSpecifyViewAndProperty();
+ }
+
+ parameters.viewName = this.View;
+ }
+
if (_props != null)
{
ParameterProcessor processor = new(new FormatObjectParameterDefinition());
@@ -68,15 +86,17 @@ internal override FormattingCommandLineParameters GetCommandLineParameters()
parameters.mshParameterList = processor.ProcessParameters(_props, invocationContext);
}
- if (!string.IsNullOrEmpty(this.View))
+ if (ExcludeProperty is not null)
{
- // we have a view command line switch
- if (parameters.mshParameterList.Count != 0)
+ parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
+
+ // ExcludeProperty implies -Property * for better UX
+ if (_props is null || _props.Length == 0)
{
- ReportCannotSpecifyViewAndProperty();
+ ParameterProcessor processor = new(new FormatObjectParameterDefinition());
+ TerminatingErrorContext invocationContext = new(this);
+ parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
}
-
- parameters.viewName = this.View;
}
parameters.groupByParameter = this.ProcessGroupByParameter();
diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs
index aeddf498f44..c6aef5c20be 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/FormatAndOutput/format-wide/Format-Wide.cs
@@ -42,9 +42,14 @@ public object Property
private object _prop;
///
- /// Optional, non positional parameter.
+ /// Gets or sets the properties to exclude from formatting.
+ ///
+ [Parameter]
+ public string[] ExcludeProperty { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to autosize the output.
///
- ///
[Parameter]
public SwitchParameter AutoSize
{
@@ -74,6 +79,18 @@ internal override FormattingCommandLineParameters GetCommandLineParameters()
{
FormattingCommandLineParameters parameters = new();
+ // Check View conflicts first (before any auto-expansion)
+ if (!string.IsNullOrEmpty(this.View))
+ {
+ // View cannot be used with Property or ExcludeProperty
+ if (_prop is not null || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
+ {
+ ReportCannotSpecifyViewAndProperty();
+ }
+
+ parameters.viewName = this.View;
+ }
+
if (_prop != null)
{
ParameterProcessor processor = new(new FormatWideParameterDefinition());
@@ -81,15 +98,17 @@ internal override FormattingCommandLineParameters GetCommandLineParameters()
parameters.mshParameterList = processor.ProcessParameters(new object[] { _prop }, invocationContext);
}
- if (!string.IsNullOrEmpty(this.View))
+ if (ExcludeProperty is not null)
{
- // we have a view command line switch
- if (parameters.mshParameterList.Count != 0)
+ parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
+
+ // ExcludeProperty implies -Property * for better UX
+ if (_prop is null)
{
- ReportCannotSpecifyViewAndProperty();
+ ParameterProcessor processor = new(new FormatWideParameterDefinition());
+ TerminatingErrorContext invocationContext = new(this);
+ parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
}
-
- parameters.viewName = this.View;
}
// we cannot specify -column and -autosize, they are mutually exclusive
diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs
index 9db27335da5..e0b0bff07c5 100644
--- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs
+++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs
@@ -12,47 +12,6 @@
namespace Microsoft.PowerShell.Commands
{
- ///
- /// Helper class to do wildcard matching on PSPropertyExpressions.
- ///
- internal sealed class PSPropertyExpressionFilter
- {
- ///
- /// Initializes a new instance of the class
- /// with the specified array of patterns.
- ///
- /// Array of pattern strings to use.
- internal PSPropertyExpressionFilter(string[] wildcardPatternsStrings)
- {
- ArgumentNullException.ThrowIfNull(wildcardPatternsStrings);
-
- _wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length];
- for (int k = 0; k < wildcardPatternsStrings.Length; k++)
- {
- _wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase);
- }
- }
-
- ///
- /// Try to match the expression against the array of wildcard patterns.
- /// The first match shortcircuits the search.
- ///
- /// PSPropertyExpression to test against.
- /// True if there is a match, else false.
- internal bool IsMatch(PSPropertyExpression expression)
- {
- for (int k = 0; k < _wildcardPatterns.Length; k++)
- {
- if (_wildcardPatterns[k].IsMatch(expression.ToString()))
- return true;
- }
-
- return false;
- }
-
- private readonly WildcardPattern[] _wildcardPatterns;
- }
-
internal sealed class SelectObjectExpressionParameterDefinition : CommandParameterDefinition
{
protected override void SetEntries()
diff --git a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs
index 18b74cbcfe1..683553a3b30 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommand.cs
@@ -9,6 +9,7 @@
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Runspaces;
+using Microsoft.PowerShell.Commands;
namespace Microsoft.PowerShell.Commands.Internal.Format
{
@@ -729,6 +730,12 @@ public class OuterFormatTableAndListBase : OuterFormatShapeCommandBase
[Parameter(Position = 0)]
public object[] Property { get; set; }
+ ///
+ /// Optional parameter for excluding properties from formatting.
+ ///
+ [Parameter]
+ public string[] ExcludeProperty { get; set; }
+
#endregion
internal override FormattingCommandLineParameters GetCommandLineParameters()
@@ -751,6 +758,18 @@ internal override FormattingCommandLineParameters GetCommandLineParameters()
internal void GetCommandLineProperties(FormattingCommandLineParameters parameters, bool isTable)
{
+ // Check View conflicts first (before any auto-expansion)
+ if (!string.IsNullOrEmpty(this.View))
+ {
+ // View cannot be used with Property or ExcludeProperty
+ if ((Property is not null && Property.Length != 0) || (ExcludeProperty is not null && ExcludeProperty.Length != 0))
+ {
+ ReportCannotSpecifyViewAndProperty();
+ }
+
+ parameters.viewName = this.View;
+ }
+
if (Property != null)
{
CommandParameterDefinition def;
@@ -765,15 +784,21 @@ internal void GetCommandLineProperties(FormattingCommandLineParameters parameter
parameters.mshParameterList = processor.ProcessParameters(Property, invocationContext);
}
- if (!string.IsNullOrEmpty(this.View))
+ if (ExcludeProperty is not null)
{
- // we have a view command line switch
- if (parameters.mshParameterList.Count != 0)
+ parameters.excludePropertyFilter = new PSPropertyExpressionFilter(ExcludeProperty);
+
+ // ExcludeProperty implies -Property * for better UX
+ if (Property is null || Property.Length == 0)
{
- ReportCannotSpecifyViewAndProperty();
- }
+ CommandParameterDefinition def = isTable
+ ? new FormatTableParameterDefinition()
+ : new FormatListParameterDefinition();
+ ParameterProcessor processor = new ParameterProcessor(def);
+ TerminatingErrorContext invocationContext = new TerminatingErrorContext(this);
- parameters.viewName = this.View;
+ parameters.mshParameterList = processor.ProcessParameters(new object[] { "*" }, invocationContext);
+ }
}
}
}
diff --git a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommandParameters.cs b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommandParameters.cs
index 7f88727ace7..498f6098a24 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommandParameters.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/BaseFormattingCommandParameters.cs
@@ -70,6 +70,11 @@ internal sealed class FormattingCommandLineParameters
/// Extension mechanism for shape specific parameters.
///
internal ShapeSpecificParameters shapeParameters = null;
+
+ ///
+ /// Filter for excluding properties from formatting.
+ ///
+ internal PSPropertyExpressionFilter excludePropertyFilter = null;
}
///
diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator.cs
index db1bc0704aa..cb20e708cc4 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System.Collections.Generic;
+using System.Linq;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Internal;
@@ -348,7 +349,19 @@ protected class DataBaseInfo
protected DataBaseInfo dataBaseInfo = new DataBaseInfo();
protected List activeAssociationList = null;
- protected FormattingCommandLineParameters inputParameters = null;
+ ///
+ /// Apply ExcludeProperty filter to activeAssociationList if specified.
+ /// This method filters and updates "activeAssociationList" instance property.
+ ///
+ protected void ApplyExcludePropertyFilter()
+ {
+ if (this.parameters is not null && this.parameters.excludePropertyFilter is not null)
+ {
+ this.activeAssociationList = this.activeAssociationList
+ .Where(item => !this.parameters.excludePropertyFilter.IsMatch(item.ResolvedExpression))
+ .ToList();
+ }
+ }
protected string GetExpressionDisplayValue(PSObject so, int enumerationLimit, PSPropertyExpression ex,
FieldFormattingDirective directive)
diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs
index b2bbd27c2b9..972a7160830 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Complex.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Internal;
@@ -16,7 +17,6 @@ internal override void Initialize(TerminatingErrorContext errorContext, PSProper
PSObject so, TypeInfoDataBase db, FormattingCommandLineParameters parameters)
{
base.Initialize(errorContext, expressionFactory, so, db, parameters);
- this.inputParameters = parameters;
}
internal override FormatStartData GenerateStartData(PSObject so)
@@ -40,7 +40,7 @@ internal override FormatEntryData GeneratePayload(PSObject so, int enumerationLi
private ComplexViewEntry GenerateComplexViewEntryFromProperties(PSObject so, int enumerationLimit)
{
ComplexViewObjectBrowser browser = new ComplexViewObjectBrowser(this.ErrorManager, this.expressionFactory, enumerationLimit);
- return browser.GenerateView(so, this.inputParameters);
+ return browser.GenerateView(so, this.parameters);
}
private ComplexViewEntry GenerateComplexViewEntryFromDataBaseInfo(PSObject so, int enumerationLimit)
@@ -425,17 +425,18 @@ internal ComplexViewObjectBrowser(FormatErrorManager resultErrorManager, PSPrope
/// of the object.
///
/// Object to process.
- /// Parameters from the command line.
+ /// Parameters from the command line.
/// Complex view entry to send to the output command.
- internal ComplexViewEntry GenerateView(PSObject so, FormattingCommandLineParameters inputParameters)
+ internal ComplexViewEntry GenerateView(PSObject so, FormattingCommandLineParameters parameters)
{
- _complexSpecificParameters = (ComplexSpecificParameters)inputParameters.shapeParameters;
+ _parameters = parameters;
+ _complexSpecificParameters = (ComplexSpecificParameters)parameters.shapeParameters;
int maxDepth = _complexSpecificParameters.maxDepth;
TraversalInfo level = new TraversalInfo(0, maxDepth);
List mshParameterList = null;
- mshParameterList = inputParameters.mshParameterList;
+ mshParameterList = parameters.mshParameterList;
// create a top level entry as root of the tree
ComplexViewEntry cve = new ComplexViewEntry();
@@ -513,6 +514,14 @@ private void DisplayObject(PSObject so, TraversalInfo currentLevel, List activeAssociationList =
AssociationManager.SetupActiveProperties(parameterList, so, _expressionFactory);
+ // Apply ExcludeProperty filter if specified
+ if (_parameters != null && _parameters.excludePropertyFilter != null)
+ {
+ activeAssociationList = activeAssociationList
+ .Where(item => !_parameters.excludePropertyFilter.IsMatch(item.ResolvedExpression))
+ .ToList();
+ }
+
// create a format entry
FormatEntry fe = new FormatEntry();
formatValueList.Add(fe);
@@ -758,6 +767,7 @@ private List AddIndentationLevel(List formatValueList)
return feFrame.formatValueList;
}
+ private FormattingCommandLineParameters _parameters;
private ComplexSpecificParameters _complexSpecificParameters;
///
diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_List.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_List.cs
index f7acd9ed226..6ef1b24ce14 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_List.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_List.cs
@@ -31,7 +31,7 @@ internal override void Initialize(TerminatingErrorContext errorContext, PSProper
_listBody = (ListControlBody)this.dataBaseInfo.view.mainControl;
}
- this.inputParameters = parameters;
+ this.parameters = parameters;
SetUpActiveProperties(so);
}
@@ -227,10 +227,12 @@ private void SetUpActiveProperties(PSObject so)
{
List mshParameterList = null;
- if (this.inputParameters != null)
- mshParameterList = this.inputParameters.mshParameterList;
+ if (this.parameters != null)
+ mshParameterList = this.parameters.mshParameterList;
this.activeAssociationList = AssociationManager.SetupActiveProperties(mshParameterList, so, this.expressionFactory);
+
+ ApplyExcludePropertyFilter();
}
}
}
diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs
index 0ddc307b646..33d2bd53506 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Table.cs
@@ -40,40 +40,45 @@ internal override void Initialize(TerminatingErrorContext errorContext, PSProper
rawMshParameterList = parameters.mshParameterList;
// check if we received properties from the command line
- if (rawMshParameterList != null && rawMshParameterList.Count > 0)
+ if (rawMshParameterList is not null && rawMshParameterList.Count > 0)
{
this.activeAssociationList = AssociationManager.ExpandTableParameters(rawMshParameterList, so);
- return;
}
-
- // we did not get any properties:
- // try to get properties from the default property set of the object
- this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
- if (this.activeAssociationList.Count > 0)
+ else
{
- // we got a valid set of properties from the default property set..add computername for
- // remoteobjects (if available)
- if (PSObjectHelper.ShouldShowComputerNameProperty(so))
+ // we did not get any properties:
+ // try to get properties from the default property set of the object
+ this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
+ if (this.activeAssociationList.Count > 0)
{
- activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null,
- new PSPropertyExpression(RemotingConstants.ComputerNameNoteProperty)));
+ // we got a valid set of properties from the default property set..add computername for
+ // remoteobjects (if available)
+ if (PSObjectHelper.ShouldShowComputerNameProperty(so))
+ {
+ activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null,
+ new PSPropertyExpression(RemotingConstants.ComputerNameNoteProperty)));
+ }
+ }
+ else
+ {
+ // we failed to get anything from the default property set
+ this.activeAssociationList = AssociationManager.ExpandAll(so);
+ if (this.activeAssociationList.Count > 0)
+ {
+ // Remove PSComputerName and PSShowComputerName from the display as needed.
+ AssociationManager.HandleComputerNameProperties(so, activeAssociationList);
+ FilterActiveAssociationList();
+ }
+ else
+ {
+ // we were unable to retrieve any properties, so we leave an empty list
+ this.activeAssociationList = new List();
+ return;
+ }
}
-
- return;
- }
-
- // we failed to get anything from the default property set
- this.activeAssociationList = AssociationManager.ExpandAll(so);
- if (this.activeAssociationList.Count > 0)
- {
- // Remove PSComputerName and PSShowComputerName from the display as needed.
- AssociationManager.HandleComputerNameProperties(so, activeAssociationList);
- FilterActiveAssociationList();
- return;
}
- // we were unable to retrieve any properties, so we leave an empty list
- this.activeAssociationList = new List();
+ ApplyExcludePropertyFilter();
}
///
diff --git a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Wide.cs b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Wide.cs
index 26c0af670c7..a45c006e7fc 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Wide.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/FormatViewGenerator_Wide.cs
@@ -13,7 +13,6 @@ internal override void Initialize(TerminatingErrorContext errorContext, PSProper
PSObject so, TypeInfoDataBase db, FormattingCommandLineParameters parameters)
{
base.Initialize(errorContext, expressionFactory, so, db, parameters);
- this.inputParameters = parameters;
}
internal override FormatStartData GenerateStartData(PSObject so)
@@ -159,39 +158,37 @@ private WideViewEntry GenerateWideViewEntryFromProperties(PSObject so, int enume
private void SetUpActiveProperty(PSObject so)
{
- List rawMshParameterList = null;
-
- if (this.inputParameters != null)
- rawMshParameterList = this.inputParameters.mshParameterList;
+ List rawMshParameterList = this.parameters?.mshParameterList;
// check if we received properties from the command line
- if (rawMshParameterList != null && rawMshParameterList.Count > 0)
+ if (rawMshParameterList is not null && rawMshParameterList.Count > 0)
{
this.activeAssociationList = AssociationManager.ExpandParameters(rawMshParameterList, so);
- return;
- }
-
- // we did not get any properties:
- // try to get the display property of the object
- PSPropertyExpression displayNameExpression = PSObjectHelper.GetDisplayNameExpression(so, this.expressionFactory);
- if (displayNameExpression != null)
- {
- this.activeAssociationList = new List();
- this.activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null, displayNameExpression));
- return;
}
-
- // try to get the default property set (we will use the first property)
- this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
- if (this.activeAssociationList.Count > 0)
+ else
{
- // we got a valid set of properties from the default property set
- return;
+ // we did not get any properties:
+ // try to get the display property of the object
+ PSPropertyExpression displayNameExpression = PSObjectHelper.GetDisplayNameExpression(so, this.expressionFactory);
+ if (displayNameExpression is not null)
+ {
+ this.activeAssociationList = new List();
+ this.activeAssociationList.Add(new MshResolvedExpressionParameterAssociation(null, displayNameExpression));
+ }
+ else
+ {
+ // try to get the default property set (we will use the first property)
+ this.activeAssociationList = AssociationManager.ExpandDefaultPropertySet(so, this.expressionFactory);
+ if (this.activeAssociationList.Count == 0)
+ {
+ // we failed to get anything from the default property set
+ // just get all the properties
+ this.activeAssociationList = AssociationManager.ExpandAll(so);
+ }
+ }
}
- // we failed to get anything from the default property set
- // just get all the properties
- this.activeAssociationList = AssociationManager.ExpandAll(so);
+ ApplyExcludePropertyFilter();
}
}
}
diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs
index 552d511fd4e..55ee3c30ddb 100644
--- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs
+++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs
@@ -375,5 +375,42 @@ private static PSObject IfHashtableWrapAsPSCustomObject(PSObject target, out boo
private bool _isResolved = false;
#endregion Private Members
+
+ }
+
+ ///
+ /// Helper class to do wildcard matching on PSPropertyExpressions.
+ ///
+ internal sealed class PSPropertyExpressionFilter
+ {
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified array of patterns.
+ ///
+ /// Array of pattern strings to use.
+ internal PSPropertyExpressionFilter(string[] wildcardPatternsStrings)
+ {
+ ArgumentNullException.ThrowIfNull(wildcardPatternsStrings);
+
+ _wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length];
+ for (int k = 0; k < wildcardPatternsStrings.Length; k++)
+ {
+ _wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase);
+ }
+ }
+
+ ///
+ /// Try to match the expression against the array of wildcard patterns.
+ /// The first match short-circuits the search.
+ ///
+ /// PSPropertyExpression to test against.
+ /// True if there is a match, else false.
+ internal bool IsMatch(PSPropertyExpression expression)
+ {
+ string expressionString = expression.ToString();
+ return _wildcardPatterns.Any(pattern => pattern.IsMatch(expressionString));
+ }
+
+ private readonly WildcardPattern[] _wildcardPatterns;
}
}
diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
index 335a704dd62..73406e449bf 100644
--- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
+++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
@@ -2504,7 +2504,8 @@ private static void NativeCommandArgumentCompletion(
case "Format-Table":
case "Format-Wide":
{
- if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase))
+ if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)
+ || parameterName.Equals("ExcludeProperty", StringComparison.OrdinalIgnoreCase))
{
NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]);
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Custom.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Custom.Tests.ps1
index 80aad6ef5b9..c82d8b77b8a 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Custom.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Custom.Tests.ps1
@@ -466,4 +466,48 @@ SelectScriptBlock
$ps.Invoke() -replace '\r?\n', "^" | Should -BeExactly $expectedOutput
$ps.Streams.Error | Should -BeNullOrEmpty
}
+
+ Context 'ExcludeProperty parameter' {
+ It 'Should exclude specified properties' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
+ $result = $obj | Format-Custom -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should work with wildcard patterns' {
+ $obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
+ $result = $obj | Format-Custom -ExcludeProperty Prop* | Out-String
+ $result | Should -Match 'Other'
+ $result | Should -Not -Match 'Prop1'
+ $result | Should -Not -Match 'Prop2'
+ }
+
+ It 'Should work without Property parameter (implies -Property *)' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
+ $result = $obj | Format-Custom -ExcludeProperty B | Out-String
+ $result | Should -Match 'A\s*='
+ $result | Should -Match 'C\s*='
+ $result | Should -Not -Match 'B\s*='
+ }
+
+ It 'Should work with Property parameter' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
+ $result = $obj | Format-Custom -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Match 'Country'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should handle multiple excluded properties' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
+ $result = $obj | Format-Custom -ExcludeProperty B, D | Out-String
+ $result | Should -Match 'A\s*='
+ $result | Should -Match 'C\s*='
+ $result | Should -Not -Match 'B\s*='
+ $result | Should -Not -Match 'D\s*='
+ }
+ }
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-List.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-List.Tests.ps1
index b9079b92139..342383f3880 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-List.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-List.Tests.ps1
@@ -261,4 +261,48 @@ Describe 'Format-List color tests' -Tag 'CI' {
$output = Get-Content "$TestDrive/outfile.txt" -Raw
$output.Trim().Replace("`r", "") | Should -BeExactly $expected.Replace("`r", "")
}
+
+ Context 'ExcludeProperty parameter' {
+ It 'Should exclude specified properties' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
+ $result = $obj | Format-List -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should work with wildcard patterns' {
+ $obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
+ $result = $obj | Format-List -ExcludeProperty Prop* | Out-String
+ $result | Should -Match 'Other'
+ $result | Should -Not -Match 'Prop1'
+ $result | Should -Not -Match 'Prop2'
+ }
+
+ It 'Should work without Property parameter (implies -Property *)' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
+ $result = $obj | Format-List -ExcludeProperty B | Out-String
+ $result | Should -Match 'A'
+ $result | Should -Match 'C'
+ $result | Should -Not -Match 'B'
+ }
+
+ It 'Should work with Property parameter' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
+ $result = $obj | Format-List -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Match 'Country'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should handle multiple excluded properties' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
+ $result = $obj | Format-List -ExcludeProperty B, D | Out-String
+ $result | Should -Match 'A'
+ $result | Should -Match 'C'
+ $result | Should -Not -Match 'B'
+ $result | Should -Not -Match 'D'
+ }
+ }
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Table.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Table.Tests.ps1
index 34f2245909c..d102ef8bbdf 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Table.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Table.Tests.ps1
@@ -953,4 +953,48 @@ Describe 'Table color tests' -Tag 'CI' {
$actual | Should -BeExactly $expected
}
+
+ Context 'ExcludeProperty parameter' {
+ It 'Should exclude specified properties' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
+ $result = $obj | Format-Table -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should work with wildcard patterns' {
+ $obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
+ $result = $obj | Format-Table -ExcludeProperty Prop* | Out-String
+ $result | Should -Match 'Other'
+ $result | Should -Not -Match 'Prop1'
+ $result | Should -Not -Match 'Prop2'
+ }
+
+ It 'Should work without Property parameter (implies -Property *)' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
+ $result = $obj | Format-Table -ExcludeProperty B | Out-String
+ $result | Should -Match 'A'
+ $result | Should -Match 'C'
+ $result | Should -Not -Match 'B'
+ }
+
+ It 'Should work with Property parameter' {
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
+ $result = $obj | Format-Table -Property Name, Age, City, Country -ExcludeProperty Age | Out-String
+ $result | Should -Match 'Name'
+ $result | Should -Match 'City'
+ $result | Should -Match 'Country'
+ $result | Should -Not -Match 'Age'
+ }
+
+ It 'Should handle multiple excluded properties' {
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
+ $result = $obj | Format-Table -ExcludeProperty B, D | Out-String
+ $result | Should -Match 'A'
+ $result | Should -Match 'C'
+ $result | Should -Not -Match 'B'
+ $result | Should -Not -Match 'D'
+ }
+ }
}
diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Wide.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Wide.Tests.ps1
index c81db9fa1f9..10acd699068 100644
--- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Wide.Tests.ps1
+++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Format-Wide.Tests.ps1
@@ -105,4 +105,56 @@ Describe "Format-Wide DRT basic functionality" -Tags "CI" {
$result | Should -Match " GroupingKey:"
$result | Should -Match "name2\s+name3"
}
+
+ Context 'ExcludeProperty parameter' {
+ It 'Should exclude specified property and display first remaining' {
+ # PSCustomObject properties are in definition order: Name, Age, City
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle' }
+ # Exclude Name, should display Age (first remaining)
+ $result = $obj | Format-Wide -ExcludeProperty Name | Out-String
+ $result | Should -Match '30'
+ $result | Should -Not -Match 'Test'
+ }
+
+ It 'Should work with wildcard patterns' {
+ # Properties: Prop1, Prop2, Other
+ $obj = [pscustomobject]@{ Prop1 = 1; Prop2 = 2; Other = 3 }
+ # Exclude Prop*, should display Other (only remaining)
+ $result = $obj | Format-Wide -ExcludeProperty Prop* | Out-String
+ $result | Should -Match '3'
+ $result | Should -Not -Match '1'
+ $result | Should -Not -Match '2'
+ }
+
+ It 'Should work without Property parameter (implies -Property *)' {
+ # Properties: A, B, C
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3 }
+ # Exclude B, C - should display A (first remaining)
+ $result = $obj | Format-Wide -ExcludeProperty B, C | Out-String
+ $result | Should -Match '1'
+ $result | Should -Not -Match '2'
+ $result | Should -Not -Match '3'
+ }
+
+ It 'Should display first remaining property after exclusion' {
+ # Properties: Name, Age, City, Country
+ $obj = [pscustomobject]@{ Name = 'Test'; Age = 30; City = 'Seattle'; Country = 'USA' }
+ # Exclude Name and Age, should display City (first remaining)
+ $result = $obj | Format-Wide -ExcludeProperty Name, Age | Out-String
+ $result | Should -Match 'Seattle'
+ $result | Should -Not -Match 'Test'
+ $result | Should -Not -Match '30'
+ # USA might appear but not in the wide field, or might not appear at all since only first property is shown
+ }
+
+ It 'Should handle multiple excluded properties' {
+ # Properties: A, B, C, D
+ $obj = [pscustomobject]@{ A = 1; B = 2; C = 3; D = 4 }
+ # Exclude A and B, should display C (first remaining)
+ $result = $obj | Format-Wide -ExcludeProperty A, B | Out-String
+ $result | Should -Match '3'
+ $result | Should -Not -Match '1'
+ $result | Should -Not -Match '2'
+ }
+ }
}