On Sun, 1/18/2026 1:17 PM, Terry ( BT) wrote:
VanguardLH <V@nguard.LH> wrote:
"Terry ( BT)" <t.pinnell@btinternet.com> wrote:
VanguardLH <V@nguard.LH> wrote:
I suspect you could copy
the entire folder with those files to a FAT drive, then copy them back >>>> to mass delete the ADS attributes on them.
Thanks for the detailed reply.
I will test your suspicion shortly. The folder I need to clean is 380 GB >>> so if your suspicion proves accurate I'll do it in six 64 GB chunks.
Pleased to report that your suspicion was correct, it works, thanks!
When copying the files around, yes, size matters.
However, when using the Nirsoft or streams tools, you are not copying,
but modifying attributes of the file in the file system. How long
streams takes to strip ADSes depends on the number of files.
Also, with copying, you risk a bad or interrupted transfer that could
corrupt files. Plus, you would be copying files that do not have an
ADS. You said only some files cause the warning, not all of them. With
Nirsoft, you see which ones were Internet zone flagged, and just remove
the ADS from just those files. streams would process every file, but
there would be nothing to do on files without an ADS. While an ADS has
content (which could be larger than those used for zones, like an entire
document or .exe stored in an ADS), deleting an ADS is faster than
copying the file twice (to FAT and back to NTFS).
But as per my original post I had tried all other methods, including
that one, in vain.
The only two methods that work for me are my Robocopy routine and your
NTFS to a PAT32 USB stick. I'm making the finishing touches to the
former so that I can automate it to clean that PITA 'Mark Of The Web'
across all of my 380 GB folder.
Terry
$PSVersionTable.PSVersion
Major Minor Build Revision
----- ----- ----- --------
5 1 26100 7462 That would be a Powershell 5.1
*******
Before we get into the following code, we need to dump the parameter list.
Or at least, for the StackOverflow I consulted, this allows verifying
what the Method definitions are. In the end, it turned out this didn't
work, but it still needs a comment about the technique.
PS D:\> $ExecutionContext.InvokeProvider.Item | Get-Member -MemberType Methods
TypeName: System.Management.Automation.ItemCmdletProviderIntrinsics
Name MemberType Definition
---- ---------- ----------
Remove Method void Remove( string[] path,
bool recurse, <=== in case the object is a directory
bool force,
bool literalPath), <=== spaces in name, punctuation...
void Remove(string path, bool recurse)
Here is a piece of code to do it. The command that removes the stream,
is currently commented out with the "#". When you feel confident the
code works, that is when you remove the "#" and run it for real.
You edit the constants in the "delstr.ps1" to suit the drive letter
and path. In my example, I just scanned all of D: (my scratch drive).
But, by NOT using the removal function, you can walk the tree and
review where the script is going. If there were to be, for example,
a file that had the permissions set to prevent modification, then that
would throw an error, but the script will still run the entire tree.
When I copy these from the web, I alter them just enough to get
them to run. The code chunk is the one at the bottom. I'm looking for Zone.Identifier on the entire D: drive . I do not know, if the error
message formatting is the best, it is likely to be a mess if an error happens.
https://stackoverflow.com/questions/75442886/remove-alternative-data-stream-using-powershell
Well, the code as given, didn't work. You can see some of the commented-out code
I tried.
******************************** delstr.ps1 prototype *******************************
$removeFunc = $ExecutionContext.InvokeProvider.Item.Remove
$targetStream = 'Zone.Identifier'
Get-ChildItem D:\ -Recurse | ForEach-Object {
$keepme = $_
if ($stream = $_ | Get-Item -Stream $targetStream -ErrorAction SilentlyContinue) {
$short = Convert-Path $stream.PSPath
$namefull = $_ | Select-Object -ExpandProperty FullName
try {
# write-output $stream.PSPath " and also " $namefull " see " $short
# maximum abspath abspath:altstream
# $removeFunc.Invoke($stream.PSPath, $false, $true, $true)
# $removeFunc.Invoke(${short}, $false, $true, $true)
# $removeFunc.Invoke(${short}, $false)
Remove-Item -LiteralPath $namefull -Stream $targetStream
Write-Output "Removed ${short}"
}
catch {
Write-Host "Error at ${short}: $($_.Exception.Message)"
}
}
}
******************************** end delstr.ps1 prototype *******************************
If we clean it up a bit (the <cough> production version), it looks like this.
******************************** delstr.ps1 *******************************
$targetStream = 'Zone.Identifier'
Get-ChildItem D:\ -Recurse | ForEach-Object {
$namefull = $_ | Select-Object -ExpandProperty FullName
# Write-Output "Visiting ${namefull}"
if ($stream = $_ | Get-Item -Stream $targetStream -ErrorAction SilentlyContinue) {
$short = Convert-Path $stream.PSPath
try {
# Remove-Item -LiteralPath $namefull -Stream $targetStream
Write-Output "Removed ${short}"
}
catch {
Write-Host "Error at ${short}: $($_.Exception.Message)"
}
}
}
# You can remove the "#" on "Visiting" line to generate a file list
# using the currently defined path ("D: " in the example).
# Remove the "#" in front of Remove-Item to actually Remove-Item the $targetStream.
******************************** end delstr.ps1 *******************************
Don't forget to change the drive letter (or folder path if tree-shaped).
Paul
--- PyGate Linux v1.5.2
* Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)