Power Shell 中是否有任何命令行用于管理附加到文件夹和卷的备用数据流?

Power Shell 中是否有任何命令行用于管理附加到文件夹和卷的备用数据流?

我知道 -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 /RPowerShell 中的输出)。

不幸的是,dir /R不能与/Bswitch 一起使用。此外,

  • 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”时,我的测试中没有删除任何子项。

小心:当您进行实验时,请确保指定参数-StreamRemove-Item否则您可能会删除卷上的所有内容。

相关内容