I was just wondering if there’s a way to search and replace text in a file without using foreach-object. I don’t understand what get-content returns. On the one hand, it’s an array of strings. But on the other hand, it’s an object array with 7 properties and no string. So you should be able to pass an altered string and PSPath down the pipe to set-content. How do you make your own object like that, with an object stream and a string stream?
(get-content input) | select @{n = '_'; e = {$_ -replace 111,222}}, PSPath | set-content # converts the hashtable to a string
or
(get-content input) | set-content -value { $_ -replace 111,222} # takes the script block as a literal string
But neither of them work…
If I just do this, I lose the path:
(get-content input) -replace 111,222 | set-content
set-content : The input object cannot be bound because it did not contain the information required to bind all mandatory parameters: Path
I feel like [string] is actually an object, but there’s a custom view that makes it look like a string, similar to get-date or [datetime]. But which .format.ps1xml defines the custom view for string?
You can add NoteProperties to any object in PowerShell, even simple things like strings and numbers, thanks to its Extended Type System. In brief, every object you handle in PowerShell is always wrapped in a PSObject. These objects have a looser definition of what a property or a method is than, for example, C#, where the object’s class definition is unchangeable once the code is compiled. As a result, you can just create and add properties to any object in PowerShell via their hidden .PSObject member.
For example:
Those are still strings you get from Get-Content… it just tacks on additional properties so that if you need it, you’ve a reference back to the file it came from.
In order to do this in a pipeline, you would need to use the Add-Member -PassThru command to add the property on and pass the resulting object through the pipeline.
So with the get-content output, how do I alter the string without losing the hidden properties like PSPath that it comes with? Only the Length property is left after using the replace operator.
If needed, you could chain addition Add-Member calls to add additional properties, or you could refer to Get-Help Add-Member -Online and check out the examples where they add multiple properties in the one Add-Member call.
Within the foreach-object, $_is the string value. It doesn’t matter to things that work with only strings that it has other secret properties, they won’t see them.
As I said… everything in PS is not simple data, it’s always a complex object. When you do $var = “string” what you’re actually doing behind the scenes is creating a string, then creating a PSObject to hold that string, and then storing that in your variable. So adding random properties to the PSObject that holds the string is done in about the same way PS adds properties to any PSObject, they’re basically a dictionary collection of property names and values and nothing more. PowerShell just behaves like those dictionary entries are real properties.
As for “which property” the string value is stored in, I guess you could say it’s technically stored in $object.PSObject.BaseObject, but at the end of the day accessing the object itself without targeting a specific property will generally cause the bare string to be used anyway.
Bit confusing, but that’s how they chose to do it. Pretty intuitive on the surface, till you hit weirdness like that, which doesn’t make a lot of sense coming from other programming languages.
Ah, yeah. Since it’s taking the file path over the pipeline, it’s not able to be sure it needs to keep the file open and write all the data. So it’s overwriting the file for each line.
Might be best to use Add-Content for this kind of usage.
Yeah, it has to be provided the path via the -Path parameter directly to be able to properly utilise the pipeline for writing a whole file, really.
You could opt to use Get-Content -Raw to retrieve the file as a single complete string and then do the replace on that before writing the file if you really want to use Set-Content over Add-Content for whatever reason.