8000 Add QuoteFields parameter to ConvertTo-Csv and Export-Csv by iSazonov · Pull Request #9132 · PowerShell/PowerShell · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ public abstract class BaseCsvWritingCommand : PSCmdlet
[Alias("NTI")]
public SwitchParameter NoTypeInformation { get; set; } = true;

/// <summary>
/// Gets or sets list of fields to quote in output.
/// </summary>
[Parameter]
[Alias("QF")]
public string[] QuoteFields { get; set; }

/// <summary>
/// Gets or sets option to use or suppress quotes in output.
/// </summary>
Expand Down Expand Up @@ -101,6 +108,13 @@ public virtual void WriteCsvLine(string line)
/// </summary>
protected override void BeginProcessing()
{
if (this.MyInvocation.BoundParameters.ContainsKey(nameof(QuoteFields)) && this.MyInvocation.BoundParameters.ContainsKey(nameof(UseQuotes)))
{
InvalidOperationException exception = new InvalidOperationException(CsvCommandStrings.CannotSpecifyQuoteFieldsAndUseQuotes);
ErrorRecord errorRecord = new ErrorRecord(exception, "CannotSpecifyQuoteFieldsAndUseQuotes", ErrorCategory.InvalidData, null);
this.ThrowTerminatingError(errorRecord);
}

if (this.MyInvocation.BoundParameters.ContainsKey(nameof(IncludeTypeInformation)) && this.MyInvocation.BoundParameters.ContainsKey(nameof(NoTypeInformation)))
{
InvalidOperationException exception = new InvalidOperationException(CsvCommandStrings.CannotSpecifyIncludeTypeInformationAndNoTypeInformation);
Expand Down Expand Up @@ -245,7 +259,7 @@ protected override void BeginProcessing()

CreateFileStream();

_helper = new ExportCsvHelper(base.Delimiter, base.UseQuotes);
_helper = new ExportCsvHelper(base.Delimiter, base.UseQuotes, base.QuoteFields);
}

/// <summary>
Expand Down Expand Up @@ -672,7 +686,7 @@ public sealed class ConvertToCsvCommand : BaseCsvWritingCommand
protected override void BeginProcessing()
{
base.BeginProcessing();
_helper = new ExportCsvHelper(base.Delimiter, base.UseQuotes);
_helper = new ExportCsvHelper(base.Delimiter, base.UseQuotes, base.QuoteFields);
}

/// <summary>
Expand Down Expand Up @@ -840,17 +854,20 @@ internal class ExportCsvHelper : IDisposable
{
private char _delimiter;
readonly private BaseCsvWritingCommand.QuoteKind _quoteKind;
readonly private HashSet<string> _quoteFields;
readonly private StringBuilder _outputString;

/// <summary>
/// Create ExportCsvHelper instance.
/// </summary>
/// <param name="delimiter">Delimiter char.</param>
/// <param name="quoteKind">Kind of quoting.</param>
internal ExportCsvHelper(char delimiter, BaseCsvWritingCommand.QuoteKind quoteKind)
/// <param name="quoteFields">List of fields to quote.</param>
internal ExportCsvHelper(char delimiter, BaseCsvWritingCommand.QuoteKind quoteKind, string[] quoteFields)
{
_delimiter = delimiter;
_quoteKind = quoteKind;
_quoteFields = quoteFields == null ? null : new HashSet<string>(quoteFields, StringComparer.OrdinalIgnoreCase);
_outputString = new StringBuilder(128);
}

Expand Down Expand Up @@ -906,25 +923,39 @@ internal string ConvertPropertyNamesCSV(IList<string> propertyNames)
_outputString.Append(_delimiter);
}

switch (_quoteKind)
if (_quoteFields != null)
{
case BaseCsvWritingCommand.QuoteKind.Always:
if (_quoteFields.TryGetValue(propertyName, out _))
{
AppendStringWithEscapeAlways(_outputString, propertyName);
break;
case BaseCsvWritingCommand.QuoteKind.AsNeeded:
if (propertyName.Contains(_delimiter))
{
}
else
{
_outputString.Append(propertyName);
}
}
else
{
switch (_quoteKind)
{
case BaseCsvWritingCommand.QuoteKind.Always:
AppendStringWithEscapeAlways(_outputString, propertyName);
}
else
{
_outputString.Append(propertyName);
}
break;
case BaseCsvWritingCommand.QuoteKind.AsNeeded:
if (propertyName.Contains(_delimiter))
{
AppendStringWithEscapeAlways(_outputString, propertyName);
}
else
{
_outputString.Append(propertyName);
}

break;
case BaseCsvWritingCommand.QuoteKind.Never:
_outputString.Append(propertyName);
break;
break;
case BaseCsvWritingCommand.QuoteKind.Never:
_outputString.Append(propertyName);
break;
}
}
}

Expand Down Expand Up @@ -963,28 +994,42 @@ internal string ConvertPSObjectToCSV(PSObject mshObject, IList<string> propertyN
{
var value = GetToStringValueForProperty(property);

switch (_quoteKind)
if (_quoteFields != null)
{
case BaseCsvWritingCommand.QuoteKind.Always:
if (_quoteFields.TryGetValue(propertyName, out _))
{
AppendStringWithEscapeAlways(_outputString, value);
break;
case BaseCsvWritingCommand.QuoteKind.AsNeeded:
if (value.Contains(_delimiter))
{
}
else
{
_outputString.Append(value);
}
}
else
{
switch (_quoteKind)
{
case BaseCsvWritingCommand.QuoteKind.Always:
AppendStringWithEscapeAlways(_outputString, value);
}
else
{
_outputString.Append(value);
}
break;
case BaseCsvWritingCommand.QuoteKind.AsNeeded:
if (value.Contains(_delimiter))
{
AppendStringWithEscapeAlways(_outputString, value);
}
else
{
_outputString.Append(value);
}

break;
case BaseCsvWritingCommand.QuoteKind.Never:
_outputString.Append(value);
break;
default:
Diagnostics.Assert(false, "BaseCsvWritingCommand.QuoteKind has new item.");
break;
break;
case BaseCsvWritingCommand.QuoteKind.Never:
_outputString.Append(value);
break;
default:
Diagnostics.Assert(false, "BaseCsvWritingCommand.QuoteKind has new item.");
break;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@
Reviewed by TArcher on 2010-06-29.
</comment>
</data>
<data name="CannotSpecifyQuoteFieldsAndUseQuotes" xml:space="preserve">
<value>You must specify either the -UseQuotes or -QuoteFields parameters, but not both.</value>
</data>
<data name="CannotSpecifyIncludeTypeInformationAndNoTypeInformation" xml:space="preserve">
<value>You must specify either the -IncludeTypeInformation or -NoTypeInformation parameters, but not both.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,31 @@ Describe "ConvertTo-Csv" -Tags "CI" {
$result | Should -Not -Match ([regex]::Escape('#TYPE'))
}

It "Does not support -UseQuotes and -QuoteFields at the same time" {
{ $testObject | ConvertTo-Csv -UseQuotes Always -QuoteFields "TestFieldName" } |
Should -Throw -ErrorId "CannotSpecifyQuoteFieldsAndUseQuotes,Microsoft.PowerShell.Commands.ConvertToCsvCommand"
}

It "Does not support -IncludeTypeInformation and -NoTypeInformation at the same time" {
{ $testObject | ConvertTo-Csv -IncludeTypeInformation -NoTypeInformation } |
Should -Throw -ErrorId "CannotSpecifyIncludeTypeInformationAndNoTypeInformation,Microsoft.PowerShell.Commands.ConvertToCsvCommand"
}

Context "QuoteFields parameter" {
It "QuoteFields" {
# Use 'FiRstCoLumn' to test case insensitivity
$result = $testObject | ConvertTo-Csv -QuoteFields FiRstCoLumn -Delimiter ','

$result[0] | Should -BeExactly "`"FirstColumn`",SecondColumn"
$result[1] | Should -BeExactly "`"Hello`",World"

$result = $testObject | ConvertTo-Csv -QuoteFields FiRstCoLumn,SeCondCoLumn -Delimiter ','

$result[0] | Should -BeExactly "`"FirstColumn`",`"SecondColumn`""
$result[1] | Should -BeExactly "`"Hello`",`"World`""
}
}

Context "UseQuotes parameter" {
It "UseQuotes Always" {
$result = $testObject | ConvertTo-Csv -UseQuotes Always -Delimiter ','
Expand Down
0