You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Set-Content should not allow writing to multiple files using wildcards #6729
Currently, Set-Content writes to whatever existing files match a wildcard pattern passed to its (implied) -Path parameter.
For instance, the following will overwrite all existing *.txt files in the current directory:
'hi'|Set-Content*.txt # !! Overwrites ALL *.txt files
This behavior is risky and should be changed to match the more sensible behavior of other output-to-file cmdlets such as Out-File (and therefore the > / >> operators), which generate an error if the wildcard expression resolves to more than one file.
It is worth examining all output-to-file cmdlets on this occasion.
PowerShell Core v6.0.2 on macOS 10.13.4
PowerShell Core v6.0.2 on Ubuntu 16.04.4 LTS
PowerShell Core v6.0.2 on Microsoft Windows 10 Pro (64-bit; Version 1709, OS Build: 16299.371)
Windows PowerShell v5.1.16299.251 on Microsoft Windows 10 Pro (64-bit; Version 1709, OS Build: 16299.371)
Assuming they knew about it, I easily can see people using this to (re)initialize a set of file so I don't think it's really a bucket 3 change. Consider that
'hi' | Set-Content *.txt
is certainly much pithier than
foreach ($f in dir *.txt) { "hi" | set-content $f }
Yes, as with any breaking change that takes away functionality there is a risk that people have relied on it.
My guess is that, given that the behavior is (a) surprising to begin with and (b) is not present in Out-File / >, it's unlikely, but it's just a guess.
As for the pithiness; not quite as concise as the original, but a reasonable substitute, in my opinion, given the benefits of taking away the risky behavior:
'hi'|Set-Content (Get-Item*.txt)
Note that, due to #6057, (Get-Item *.txt).PSPath should currently be used for full robustness.
your issue is not what I thought it was. It appears that you still want Set-Content to be able to write identical content to multiple files. Is this the case?
It appears that you still want Set-Content to be able to write identical content to multiple files. Is this the case?
Yes, if you pass an array of literal file paths to -Path / -LiteralPath, which to me unambiguously signals the intent.
No change need there, because these parameters are already typed [string[]].
By contrast, the wildcard-based way of targeting multiple file is the fraught behavior, which I think should be eliminated.
I used the 'hi' | Set-Content (Get-Item *.txt) example to demonstrate that someone who truly wants to target multiple files will still be able to do so, even once the wildcard-based method is taken away.
Now you could ask what should happen if an array is passed to -Path (possibly implicitly) and at least one element of that array is a wildcard expression?
For simplicity, my vote is for disallowing any wildcard expression that matches more than 1 file, whether stand-alone or as part of an array.
To summarize, the desired behavior is:
You can pass one or more literal paths to -LiteralPath.
You can pass one or more literal paths or wildcard patterns - in any combination - to -Path, as long as no single wildcard pattern matches more than 1 file.
This seems like the sort of thing that should be the default, but able to be worked around. You've provided one workaround, but consider perhaps that we could also allow the -Force parameter to bypass this restriction. In other words:
# This doesn't work:"blank"|Set-Content-Path "*.txt"# But this does"blank"|Set-Content-Path "*.txt"-Force
Having the ability be opt-in is definitely preferable, but note that we'd be adding meaning to the existing use of -Force ("Forces the cmdlet to set the contents of a file, even if the file is read-only.")
Also, you could argue that we'd have to implement the same thing for all output-to-file cmdlets (Out-File, Export-Csv, ...)
On the other hand, as I've just realized, Set-Content already is special in that at least at first glance it appears to be the only one that supports an array of output filenames.
I think we could remove the dangerous behavior. In File System Provider V2 at least. And follow to one principle for all cmdlet - target path parameter should always be LiteralPath. (We already did this for web cmdlets and Out-File is under question too #9475.)
@PowerShell/powershell-committee looked at this and we compared it to behavior in zsh and bash. It seems that zsh does support redirection to multiple files matching the wildcard while bash complains that the destination is ambiguous. Also given that there seems to be legitimate cases where you want to initialize multiple files to the same content, then we think this is not a bucket 3 breaking change. Also, if -WhatIf is used with Set-Content it does inform you of what was matched and would be written. As noted, Out-File restricts to matching a single file so we have a bit of inconsistency between Out-File and Set-Content, but those two cmdlets don't have to be identical otherwise they'd be a single cmdlet. We believe this is By Design
Activity
BrucePay commentedon Apr 25, 2018
Assuming they knew about it, I easily can see people using this to (re)initialize a set of file so I don't think it's really a bucket 3 change. Consider that
is certainly much pithier than
mklement0 commentedon Apr 25, 2018
Yes, as with any breaking change that takes away functionality there is a risk that people have relied on it.
My guess is that, given that the behavior is (a) surprising to begin with and (b) is not present in
Out-File/>, it's unlikely, but it's just a guess.As for the pithiness; not quite as concise as the original, but a reasonable substitute, in my opinion, given the benefits of taking away the risky behavior:
Note that, due to #6057,
(Get-Item *.txt).PSPathshould currently be used for full robustness.BrucePay commentedon Apr 26, 2018
Based on
your issue is not what I thought it was. It appears that you still want
Set-Contentto be able to write identical content to multiple files. Is this the case?mklement0 commentedon Apr 26, 2018
Yes, if you pass an array of literal file paths to
-Path/-LiteralPath, which to me unambiguously signals the intent.No change need there, because these parameters are already typed
[string[]].By contrast, the wildcard-based way of targeting multiple file is the fraught behavior, which I think should be eliminated.
I used the
'hi' | Set-Content (Get-Item *.txt)example to demonstrate that someone who truly wants to target multiple files will still be able to do so, even once the wildcard-based method is taken away.Now you could ask what should happen if an array is passed to
-Path(possibly implicitly) and at least one element of that array is a wildcard expression?For simplicity, my vote is for disallowing any wildcard expression that matches more than 1 file, whether stand-alone or as part of an array.
To summarize, the desired behavior is:
You can pass one or more literal paths to
-LiteralPath.You can pass one or more literal paths or wildcard patterns - in any combination - to
-Path, as long as no single wildcard pattern matches more than 1 file.vexx32 commentedon Apr 26, 2018
This seems like the sort of thing that should be the default, but able to be worked around. You've provided one workaround, but consider perhaps that we could also allow the
-Forceparameter to bypass this restriction. In other words:mklement0 commentedon Apr 26, 2018
Rain Sallow (/u/ta11ow) (@vexx32):
Having the ability be opt-in is definitely preferable, but note that we'd be adding meaning to the existing use of
-Force("Forces the cmdlet to set the contents of a file, even if the file is read-only.")Also, you could argue that we'd have to implement the same thing for all output-to-file cmdlets (
Out-File,Export-Csv, ...)On the other hand, as I've just realized,
Set-Contentalready is special in that at least at first glance it appears to be the only one that supports an array of output filenames.iSazonov commentedon Dec 1, 2021
I think we could remove the dangerous behavior. In File System Provider V2 at least. And follow to one principle for all cmdlet - target path parameter should always be LiteralPath. (We already did this for web cmdlets and Out-File is under question too #9475.)
SteveL-MSFT commentedon Dec 1, 2021
@PowerShell/powershell-committee looked at this and we compared it to behavior in
zshandbash. It seems thatzshdoes support redirection to multiple files matching the wildcard whilebashcomplains that the destination is ambiguous. Also given that there seems to be legitimate cases where you want to initialize multiple files to the same content, then we think this is not a bucket 3 breaking change. Also, if-WhatIfis used withSet-Contentit does inform you of what was matched and would be written. As noted,Out-Filerestricts to matching a single file so we have a bit of inconsistency betweenOut-FileandSet-Content, but those two cmdlets don't have to be identical otherwise they'd be a single cmdlet. We believe this isBy DesigniSazonov commentedon Dec 2, 2021