剥离 CMD 输出以仅显示目录

Strip CMD output to just show directories

我目前正在尝试用 cmd 脚本替换 powershell 脚本,因为它更适合我正在尝试完成的工作。

在 Powershell 中,我使用这段代码 return 计算机上的个人文件夹目录列表

$Name = [Environment]::UserName
get-item HKCU:\software\Microsoft\Office.0\Outlook\Search\Catalog - ErrorAction SilentlyContinue | select -expandProperty property | Out-File Z:\global\pst\PowershellOutput$Name.txt -append

这就是我想要的,并输出一个目录列表,就像这样

H:\PST\My Outlook Data File 1.pst
H:\PST\My Outlook Data File 2.pst
C:\PST\My Outlook Data File 3.pst

但是当我运行这一行提取注册表项时

regedit.exe /e Z:\global\battest\%username%.txt "HKEY_CURRENT_USER\Software\Microsoft\Office.0\Outlook\Search\Catalog"

我得到一个包含大量不必要数据的输出

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Office.0\Outlook\Search\Catalog]
"H:\PST\My Outlook Data File 1.pst"=hex:0c,01,00,00,00,00,00,00
"H:\PST\My Outlook Data File 2.pst"=hex:f8,00,00,00,00,00,00,00
"C:\PST\My Outlook Data File 3.pst"=hex:ac,02,00,00,00,00,00,00

此数据被传递到另一个程序*,因此解决方法是使用“”标记内的数据,但它也有双反斜杠,这使得数据难以传递。

有没有更好的方法在 CMD 中获取这些值,或者我错过了一个只显示目录的参数?

您可能需要使用 REG QUERY 命令。在单个 bat 文件中,它会像:

@echo OFF
for /f "usebackq tokens=1-3" %%A in (`REG QUERY "HKCU\Software\Microsoft\Office.0\Outlook\Search\Catalog"`) do (
 set ValueName=%%A
)
echo %ValueName% > Z:\global\battest\%username%.txt

How can I get the value of a registry key from within a batch script?

得到这个

这是一个批处理代码,用于获取 *.pst 文件(不是目录)的列表,这些代码独立于 *.pst 文件的数量及其文件名,只要文件名以 .pst.

结尾
@echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "skip=1 delims=" %%# in ('%SystemRoot%\System32\reg.exe query HKCU\Software\Microsoft\Office.0\Outlook\Search\Catalog 2^>nul') do (
    set "RegData=%%#"
    set "DelData=!RegData:*.pst=!"
    if not "!DelData!" == "!RegData!" call :OutputFileName
)
endlocal
goto :EOF

:OutputFileName
set "RegData=!RegData:%DelData%=!"
echo !RegData:~4!
goto :EOF

*.pst 文件是 Outlook 个人存储文件,是电子邮件、联系人的容器文件,...

命令 FOR 运行控制台应用程序 REG 以查询注册表项的所有值:

HKEY_CURRENT_USER\Software\Microsoft\Office.0\Outlook\Search\Catalog

在 Windows Vista 和更新的 Windows 版本上的输出是为了示例:

HKEY_CURRENT_USER\Software\Microsoft\Office.0\Outlook\Search\Catalog
    H:\PST\My Outlook Data File 1.pst    REG_SZ    whatever the
    H:\PST\My Outlook Data File 2.pst    REG_SZ    data value is
    C:\PST\My Outlook Data File 3.pst    REG_SZ    of each value

输出注册表项,然后是注册表值,每个注册表值缩进 4 个空格,值名称和值类型之间有 4 个空格,值类型和数据值之间有 4 个空格。

但在 Windows XP 上,输出为示例:

! REG.EXE VERSION 3.0

HKEY_CURRENT_USER\Software\Microsoft\Office.0\Outlook\Search\Catalog
    H:\PST\My Outlook Data File 1.pst   REG_SZ  whatever the
    H:\PST\My Outlook Data File 2.pst   REG_SZ  data value is
    C:\PST\My Outlook Data File 3.pst   REG_SZ  of each value

因此在注册表项之前有前两行 header。注册表值也缩进了 4 个空格。但是值名称和值类型之间有 1 个或多个制表符而不是空格,并且值类型和数据值之间也有 1 个或多个制表符而不是空格。

注:不知道REG_SZ是不是正确的注册表值类型。我的 Windows 注册表中根本没有此注册表项,因此只添加了 3 个注册表值作为带有虚拟字符串的字符串值。

所以棘手的部分是如何获取还包含空格的注册表值名称,以获取独立于文件名的 *.pst 文件的名称。

这是通过将每个输出行分配给环境变量 skip=1 的第一行除外,然后从该行获取下一个分配给环境的 .pst 之后的所有内容变量 DelData.

如果该行在任何情况下都包含 .pst 导致 DelData 的值不等于 RegData 的值,则进一步调用子例程 OutputFileName处理并打印 *.pst 文件的文件名。

在子例程 OutputFileName 中,首先使用字符串替换从 RegData 中删除 *.pst 文件名右侧的字符串。接下来输出*.pst文件名,开头没有4个缩进空格。

如果缩进空格数不总是恰好是 4 个空格,行 echo !RegData:~4! 可以替换为以下代码以独立于缩进数 spaces/tabs.

工作
for /F "tokens=1*" %%I in ("%RegData%") do (
    if "%%J" == "" (
        echo %%I
    ) else (
        echo %%I %%J
    )
)

使用 REG 而不是 REGEDIT 的优点是 运行 不需要提升管理员权限这个批号。

要了解使用的命令及其工作原理,请打开命令提示符 window,在其中执行以下命令,并仔细阅读为每个命令显示的所有帮助页面。

  • call /?
  • echo /?
  • for /?
  • goto /?
  • reg /?
  • reg query /?
  • set /?
  • setlocal /?

如果注册表项不存在,则不输出任何内容。通过使用 2^>nul 将其重定向到设备 NUL,此处抑制了处理 STDERR 的错误消息输出。有关详细信息,请参阅 Microsoft 文章 Using command redirection operators。重定向运算符 > 必须在此处使用脱字符 ^ 进行转义,以便在解析 FOR 命令行时被解释为文字字符,并在执行时被解释为重定向运算符REG 命令行的 FOR.

虽然我没有安装 Office 进行测试,但这应该可以完成工作

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "HKCU=&H80000001"
    set "subKey=software\Microsoft\Office.0\Outlook\Search\Catalog"

    >> "Z:\global\pst\PowershellOutput\%username%.txt" (
        for /f "tokens=2 delims={" %%a in ('
            wmic 
                /NameSpace:\root\default 
                Class StdRegProv 
                Call EnumValues 
                     hDefKey^="%HKCU%" 
                     sSubKeyName^="%subkey%"
            2^>nul 
            ^| find "sNames = {"
        ') do for %%b in (%%a) do (
            for /f delims^=^" %%c in ("%%~b") do echo(%%~fc
        )
    )

它使用 wmic 检索指定键下定义的值列表,并过滤输出以仅检索具有格式为 sNames = {"v1", "v2", "v3"}.[=19 的这些值的名称的行=]

{ 用于将行的开头与值列表 (%%a) 分开,并迭代此列表 (%%b) 以获取每个值。列表中的最后一个元素包含需要删除的结尾 },这由 %%c 使用值中的引号作为分隔符处理。

等效的 vbs 版本可以是

Option Explicit

Const HKEY_CURRENT_USER = &H80000001
Const ForAppending = 8

Const SUB_KEY = "software\Microsoft\Office.0\Outlook\Search\Catalog"
Const OUTPUT_PATH = "Z:\global\pst\PowershellOutput"

Dim fso, shell
    Set fso = WScript.CreateObject("Scripting.FileSystemObject")
    Set shell = WScript.CreateObject("WScript.Shell")

Dim values
    Call GetObject( _ 
        "winmgmts:{impersonationLevel=impersonate}!\.\root\default:StdRegProv" _ 
    ).EnumValues( _ 
        HKEY_CURRENT_USER, SUB_KEY, values _ 
    )

Dim outputFile
    Set outputFile = fso.OpenTextFile( _ 
        fso.BuildPath( _ 
            OUTPUT_PATH, shell.ExpandEnvironmentStrings("%username%")  & ".txt" _ 
        ) _ 
        , ForAppending _
        , True _
    )

Dim value    
    If Not IsNull(values) Then 
        For Each value In values
            Call outputFile.WriteLine(fso.GetAbsolutePathName( value ))
        Next 
    End If

    Call outputFile.Close()