GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Join-String cmdlet for creating text from pipeline input by powercode · Pull Request #7660 · PowerShell/PowerShell Conversation
|
|
'Object' in the cmdlet name implies that we can join objects of any type - array + array, array + hash, hash + hash, strings and so on. This cmdlet is more like ConvertTo-String |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
It is intended to be the pipeline equivalent of the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
If its going to be the pipeline equivalent of -join wouldn't Join-String be a more descriptive name. Shouldn't there also be a Split-? cmdlet to match the -split operator |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
It depends. It can join objects, or the properties of objects. The output is a string, but it works with object input. Just like Group-Object and Sort-Object. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| /// </summary> | ||
| [Cmdlet(VerbsCommon.Join, "Object", RemotingCapability = RemotingCapability.None, DefaultParameterSetName = "default")] | ||
| [OutputType(typeof(string))] | ||
| public class JoinObjectCommand : PSCmdlet |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think this should be public sealed class.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
yes
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| public class JoinObjectCommand : PSCmdlet | ||
| { | ||
| // ReSharper disable once CollectionNeverQueried.Local | ||
| private readonly List<PSObject> _inputObjects = new List<PSObject>(50); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I normally don't see List size initialization unless the size is known. I am curious why you initialize the size to this value?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
It is cheaper to allocate a list that is a little larger than to reallocate and copy when out-grown.
But the size can always be argued.
When I profile memory allocations, Array allocations on List.Resize is not uncommon, and when I see them, I try to find a sensible default size do get rid of the most common once.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| namespace Microsoft.PowerShell.Commands.Utility | ||
| { | ||
| /// <summary> | ||
| /// Group-Object implementation. |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Join-Object implementation
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
oops :)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| if (PropertyName == null) | ||
| { | ||
| if (_inputObjects.Count > 0) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think you can just check count once and early out or just skip the if/else processing.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yes, much better!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| public TypeInferenceContext() | ||
| : this(PowerShell.Create(RunspaceMode.CurrentRunspace)) | ||
| { | ||
| _ownsPowerShell = true; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right to me. This class can own PowerShell/Runspace only if PowerShell is created with Runspace.NewRunspace mode. Otherwise you are disposing a runspace created by someone else (and possibly the thread default runspace).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Reverting the whole file.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Paul Higinbotham (@PaulHigin) Thx for the review. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| CommandAst commandAst, | ||
| IDictionary fakeBoundParameters) | ||
| { | ||
| var res = new List<CompletionResult>(10); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can it be static?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Then you end up with races if used from multiple runspaces.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The list looks as a const for current session. Do I skip something?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is captured by the local function AddMatching.
So the result is depending on wordToComplete.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I see now. :-) It is Friday. I can not catch up with my idea :-)
I am looking at 'new CompletionResult()'. If they isn't modifed later we could make it static and only reference in target List?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
| /// </summary> | ||
| [Parameter(Position = 0)] | ||
| [ArgumentCompleter(typeof(PropertyNameCompleter))] | ||
| public object PropertyName { get; set; } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Can we use PSPropertyExpression type like in #6934?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
PropertyExpression has some extra semantics, handling hashtables like @{n="x";ex={2,3,4}}.
I looked at it, but was not convinced it was a fit. But there's definitely an overlap. Input here is most welcome.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| case "\r": return "`r"; | ||
| case "\n": return "`n"; | ||
| case "\r\n": return "`r`n"; | ||
| default: return Environment.NewLine.Replace("\r", "`r").Replace("\n", "`n"); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Seems the switch is superfluous. We could leave only
``'c#
return Environment.NewLine.Replace("\r", "r").Replace("\n", "n");
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yes it could. I wrote it the way I did to reduce allocated objects. May not be worth it.
The string literals will be loaded from metadata and interned. There will only exist one instance of them. The default line will create two new strings each time.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Environment.NewLine is static. So I think we should use the usual method
public static string NewLineText { get { #if UNIX return "`n" #else return "`r`n" #endif } }
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Good point!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
But I think MacOS uses `r
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
No, only very old MacOs versions. See my comment below.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
A general question is how we should handle null and AutomationNull.Value in the input?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| CommandAst commandAst, | ||
| IDictionary fakeBoundParameters) | ||
| { | ||
| var res = new List<CompletionResult>(10); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yes, it is captured by the local function AddMatching.
So the result is depending on wordToComplete.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Ex.: $null,$null | % { 1 } 1 1 [System.Management.Automation.Internal.AutomationNull]::Value,[System.Management.Automation.Internal.AutomationNull]::Value | % { 1 } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Should it be named |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I'd be inclined to call it Join-String instead, since nothing of the original objects are preserved (counter to things like Group-Object or Select-Object) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
PreScript to Prefix PostScript to Suffix. PropertyName to Property.
|
Bruce Payette (@BrucePay) can you please take a look again at this PR? Thanks. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| /// <summary> | ||
| /// Gets or sets a format string that is applied to each input object. | ||
| /// </summary> |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'd expand the comment with what is the format and/or add a link on docs.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| [OutputType(typeof(string))] | ||
| public sealed class JoinStringCommand : PSCmdlet | ||
| { | ||
| /// <summary>A bigger default to not get re-allocations in common use cases.</summary> |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Please use file pattern:
/// <Summary> /// A bigger default to not get re-allocations in common use cases. /// </Summary>
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| { | ||
| _outputBuilder.Append(_quoteChar); | ||
| _outputBuilder.Append(stringValue); | ||
| _outputBuilder.Append(_quoteChar); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We could use
_outputBuilder.Append(_quoteChar).Append(stringValue).(_quoteChar);
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
| else | ||
| { | ||
| _outputBuilder.AppendFormat(CultureInfo.CurrentCulture, FormatString, stringValue); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'd want to get confirmation that this should be CurrentCulture not InvariantCulture.
And maybe discuss -FormatCulture parameter.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Same here. Now sure what is right here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
PowerShell consistently uses the invariant culture in to/from-string conversions, so it should probably be InvariantCulture, at least by default, perhaps with an optional -UseCulture switch (analogous to the *-Csv cmdlets).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
As a guideline, we use InvariantCulture when manipulating data so that the behaviour of the code is predictable (invariant) across locales and we use current culture when presenting to the user. This cmdlet could be used in both scenarios but I expect it will be used more for data processing so I suggest defaulting to InvariantCulture with an option to override for current culture.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Adding UseCulture switch as suggested.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
gcm -ParameterName Culture CommandType Name Version Source ----------- ---- ------- ------ Cmdlet Compare-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet Group-Object 3.1.0.0 Microsoft.PowerShell.Utility Cmdlet New-PSSessionOption 6.0.0.0 Microsoft.PowerShell.Core Cmdlet Sort-Object 3.1.0.0 Microsoft.PowerShell.Utility
We already have -Culture parameter. Make sense re-use the name?
Or -UseCurrentCulture? Looks as limited option.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think -UseCulture is the correct name, after all:
The -Culture <string> parameters you reference:
The -UseCulture switch, by contrast:
*-Csv cmdlets).The latter is what is being implemented here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Michael Klement (@mklement0) Thanks! We need discuss Select-String too.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| get | ||
| { | ||
| #if UNIX | ||
| return Platform.IsMacOS ? "`r" : "`n"; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Please address the comment.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
|
||
| It "Should be called using an object as piped without error with no switches" { | ||
| {$testObject | Join-String } | Should -Not -Throw | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We could remove the test - any test below do the test.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Staffan Gustafsson (@powercode) Looks like this PR still needs a couple of minor updates that are mentioned in Ilya (@iSazonov)'s review; especially InvariantCulture issue. Otherwise it seems close to being done. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Sorry - been a bit preoccupied. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I just realized that when joining dates, I always get the "US", Invariant formatting, which is literally never the output a Swede would want. To get the I will add a new commit with this change - feel free to ignore it if you don't think it should go with this PR. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
d64cc84 to
937d9b8
Compare
|
The last commit ( |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
What is current policy: don't merge until doc issue is created? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…Shell#7660) The cmdlet syntax is as follows: ``` Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-SingleQuote] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-DoubleQuote] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-FormatString <string>] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] ```
…Shell#7660) The cmdlet syntax is as follows: ``` Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-SingleQuote] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-DoubleQuote] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] Join-String [[-Property] <pspropertyexpression>] [[-Separator] <string>] [-OutputPrefix <string>] [-OutputSuffix <string>] [-FormatString <string>] [-UseCulture] [-InputObject <psobject>] [<CommonParameters>] ```
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.