我知道 -streams 参数允许您使用附加到文件的备用数据流执行很多操作,但它不适用于文件夹和卷。
答案1
PS D:\PShell> Get-Command -All -ParameterName 'stream' | Format-Table -AutoSize -Wrap
CommandType Name ModuleName
----------- ---- ----------
Cmdlet Add-Content Microsoft.PowerShell.Management
Cmdlet Clear-Content Microsoft.PowerShell.Management
Cmdlet Get-Content Microsoft.PowerShell.Management
Cmdlet Get-Item Microsoft.PowerShell.Management
Cmdlet Out-String Microsoft.PowerShell.Utility
Cmdlet Remove-Item Microsoft.PowerShell.Management
Cmdlet Set-Content Microsoft.PowerShell.Management
上述所有命令应该能够很好地处理与文件、文件夹或磁盘相关的备用数据流如果你知道流名称。 很遗憾,Get-ChildItem
命令由于不允许该-Stream
参数,因此未列出。另一方面,可以枚举文件使用交替数据流Get-Item
对于文件系统关于-Stream
参数:
从文件中获取指定的备用 NTFS 文件流。输入流名称。支持通配符。要获取所有流,请使用星号 (
*
)。此参数对文件夹无效。
无需使用外部工具streams.exe
来自 Sysinternals,你可以在命令行上使用以下方法枚举所有备用数据流dir /R
命令(Vista 及以上版本)(并解析. cmd /D /C dir /R
PowerShell 中的输出)。
不幸的是,dir /R
不能与/B
switch 一起使用。此外,
dir /R /A:-D
返回文件的所有流(常规数据文件和 ADS 名称),dir /R /A:D
返回除根文件夹/磁盘之外的文件夹的所有流(文件夹和 ADS 名称),dir /R
返回文件和文件夹的所有名称(常规和 ADS)除根文件夹/磁盘。
我写了两个.BAT
脚本来解析dir /R
和能输出格式为CSV
:
ADSdisk.bat [/CSVformat]
返回所有活动(本地)磁盘的 ADS 名称,以及ADSdir.bat [drive:][path][filename] [/S] [/A[[:]attributes]] [/CSVformat]
DIR
仅返回与-like 参数相对应的 ADS 名称。
PowerShell 中的使用示例:
PS D:\PShell> . D:\bat\ADSdisk.bat /CSVformat
"streamName",streamSize,"filePath"
"ADS_disk_root.txt",36,"D:\"
# comment: ADS on D:\ "..:ADS_disk_root.txt:$DATA"
# comment: no ADS on F:\
# comment: no ADS on C:\
# comment: no ADS on E:\
PS D:\PShell> (. D:\bat\ADSdisk.bat /CSVformat) | ConvertFrom-Csv
streamName streamSize filePath
---------- ---------- --------
ADS_disk_root.txt 36 D:\
PS D:\PShell> (. D:\bat\ADSdir.bat d:\ba* /CSVformat) | ConvertFrom-Csv
streamName streamSize filePath
---------- ---------- --------
alternate.data.stream.txt 46 d:\BAsoftwarelist.csv
alternate.data.stream.txt 23 d:\bat
PShell.txt 34 d:\bat
star.bat 12 d:\bat
star.txt 23 d:\bat
PS D:\PShell>
ADSdisk.bat
脚本:
@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_keyword=Directory"
set "_CSVformat="
set "_firstLine="
set _filePath=%*
if defined _filePath (
for %%G in ( %_filePath% ) do (
if /I %%G == /CSVformat (
set _filePath=%_filePath:/CSVformat=%
set "_CSVformat=/CSVformat"
)
)
)
set "_property=DeviceID^,DriveLetter^,DriveType^,FileSystem^,SystemName"
for /F "skip=1 tokens=1-4,*" %%G in ('
wmic volume where "FileSystem LIKE '_%%' and DriveLetter LIKE '_:'" get %_property%
') do (
if not "%%H"=="" (
rem echo(# comment: %%H %%I %%J %%G
set "_subFolder="
set "_subDisk=%%~H\"
for /F "delims=" %%g in ( 'dir /B /A:D /A /O:-N "%%~H\"') do set "_subFolder=%%~g"
if defined _subFolder (
call :findADSroot
if not defined _fileName (
call echo(# comment: no ADS on %%_subDisk%%
) else (
call echo(# comment: ADS on %%_subDisk%% "%%_fileName%%"
)
) else (
echo(# comment: no subfoler under "%%~H\"
)
)
)
ENDLOCAL
goto :eof
:findADSroot
set "_pass="
set "_filePath=%_subDisk%%_subFolder%"
rem echo(# comment: ===testing=== "%_filePath%\"
set "_fileName="
for /F "tokens=1,*" %%G in ('
dir /-C /R /S /A "%_filePath%" ^| findstr /I /R "\.\.:.*:\$DATA ^.%_keyword%"
') do (
set "_fileSize="
if "%%~G"=="%_keyword%" (
if defined _pass goto :eof
for /F "tokens=1,*" %%g in ("%%~H") do set "_fileRoot=%%~h"
set /A _pass +=1
) else (
set "_fileSize=%%~G"
set "_fileName=%%~H"
)
if defined _fileSize call :outputDisk
)
goto :eof
:outputDisk
if defined _CSVformat (
REM csv output:
if not defined _firstLine echo "streamName",streamSize,"filePath"
for /F "tokens=1-3 delims=:" %%g in ("%_fileName%") do (
echo "%%~h",%_fileSize%,"%_subDisk%"
)
) else (
REM alternative output: streamSize,"streamFullPath"
echo %_fileSize% "%_subDisk%%_fileName:~2%"
)
set /A _firstLine += 1
goto :eof
ADSdir.bat
脚本:
@ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "_keyword=Directory"
set "_CSVformat="
set "_firstLine="
set _filePath=%*
if defined _filePath (
for %%G in ( %_filePath% ) do (
if /I %%G == /CSVformat (
set _filePath=%_filePath:/CSVformat=%
set "_CSVformat=/CSVformat"
)
)
)
if not defined _filePath set "_filePath=/S "%CD%""
call :findADS
ENDLOCAL
goto :eof
:findADS
for /F "tokens=1,*" %%G in ('
dir /-C /R %_filePath% ^| findstr /I /R ":.*:\$DATA ^.%_keyword%"
') do (
set "_fileSize="
if "%%~G"=="%_keyword%" (
for /F "tokens=1,*" %%g in ("%%~H") do set "_fileRoot=%%~h"
call :backslash
) else (
set "_fileSize=%%~G"
rem for /F "tokens=1,*" %%g in ("%%~G") do
set "_fileName=%%~H"
call :testdots
)
if defined _fileSize call :output
)
goto :eof
:backslash
set "_backslash=%_fileRoot:~-1%"
if "%_backslash%"=="\" (set "_backslash=") else set "_backslash=\"
rem echo %~0 %_fileRoot:~-1%==%_backslash%
rem pause
goto :eof
:testdots
rem "." = a folder itself
if "%_fileName:~0,2%"==".:" set "_fileSize="
rem ".." = parent folder
if "%_fileName:~0,3%"=="..:" set "_fileSize="
goto :eof
:output
if defined _CSVformat (
REM csv output:
if not defined _firstLine echo "streamName",streamSize,"filePath"
for /F "tokens=1-3 delims=:" %%g in ("%_fileName%") do (
echo "%%~h",%_fileSize%,"%_fileRoot%%_backslash%%%~g"
)
) else (
REM alternative output: streamSize,"streamFullPath"
echo %_fileSize% "%_fileRoot%%_backslash%%_fileName%"
)
set /A _firstLine += 1
goto :eof
答案2
看起来是可以的。这里我把一个 ADS 添加到“d:\”,然后删除它。
> Set-Content -Path d:\ -Value "Hello, World" -Stream Fred
> Get-Content -Path d:\ -Stream Fred
Hello, World
> Remove-Item -Path d:\ -Stream Fred
Confirm
The item at D:\ has children and the Recurse parameter was not specified. If you continue, all children will be removed with the item. Are you sure you want to continue?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): y
E:\scratch\st> Get-Content -Path d:\ -Stream Fred
Get-Content : Could not open the alternate data stream 'Fred' of the file 'D:\'.
该警告似乎不正确。当我指定“Y”时,我的测试中没有删除任何子项。
小心:当您进行实验时,请确保指定参数-Stream
,Remove-Item
否则您可能会删除卷上的所有内容。