FOR 循环空间和处理

Posted

技术标签:

【中文标题】FOR 循环空间和处理【英文标题】:FOR loop spaces and handling 【发布时间】:2016-10-21 23:52:29 【问题描述】:

我有一个列出所有服务的批处理脚本,采用 BINARY_PATH,删除任何包含 c"\windows" 的行并提供 BINARY_PATHS 列表。

我们的想法是将该列表传递给 ICACLS,以确定对每个可执行文件设置的权限。

我遇到的问题是一些 BINARY_PATHS 包含前导和尾随“。 所以我不得不通过在我的 for /f 语句中添加 delims=" 来解决这个问题。

下面是输出到 ECHO 的批处理文件

for /f "tokens=2" %%n in ('sc query state^= all ^| findstr SERVICE_NAME') do (
  for /f "delims=: tokens=1*" %%r in (
    'sc qc "%%~n" ^| findstr BINARY_PATH_NAME'
  ) do (
    for /f tokens^=1-2^ delims^=^" %%x in ('echo %%~s^| findstr /V /I "c:\windows\system32"') do (
         echo "%%~x%%~y" 
        )
  )
)

ECHO 输出如下 - 一个很好的干净列表,它看起来与前导空格分开

" C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_state.exe"
" C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorsvw.exe"
" C:\Windows\Microsoft.Net\Framework\v3.0\WPF\PresentationFontCache.exe"
" C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\infoc
ard.exe"
" C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\SMSvc
Host.exe"
" C:\Program Files\Photodex\ProShow Producer\ScsiAccess.exe"
" C:\Windows\servicing\TrustedInstaller.exe"
" C:\Program Files\VMware\VMware Tools\vmtoolsd.exe"

当我尝试将输出传递给不带前导和尾随 " 的 icacls 时,它会崩溃,因为某些 PATH 中有空格。

ICACLS 批处理文件:

for /f "tokens=2" %%n in ('sc query state^= all ^| findstr SERVICE_NAME') do (
  for /f "delims=: tokens=1*" %%r in (
    'sc qc "%%~n" ^| findstr BINARY_PATH_NAME'
  ) do (
    for /f tokens^=1-2^ delims^=^" %%x in ('echo %%~s ^| findstr /V /I "c:\windows\system32"') do (
         icacls %%~x%%~y 
        )
  )
)

ICACLS 批处理的一些输出:

C:\Windows\Microsoft.Net\Framework\v3.0\WPF\PresentationFontCache.exe NT SERVICE
\TrustedInstaller:(F)
                                                                      BUILTIN\Ad
ministrators:(RX)
                                                                      NT AUTHORI
TY\SYSTEM:(RX)
                                                                      BUILTIN\Us
ers:(RX)

Successfully processed 1 files; Failed processing 0 files
Invalid parameter "Communication"
Invalid parameter "Communication"
Invalid parameter "Files\Photodex\ProShow"
C:\Windows\servicing\TrustedInstaller.exe NT SERVICE\TrustedInstaller:(F)
                                          BUILTIN\Administrators:(RX)
                                          NT AUTHORITY\SYSTEM:(RX)
                                          BUILTIN\Users:(RX)

Successfully processed 1 files; Failed processing 0 files
Invalid parameter "Files\VMware\VMware"

有什么想法吗?

【问题讨论】:

最外面的for /f循环使用变量%%n,最里面的循环也隐含地使用变量%%n,因为%%mtokens=1-2选项;虽然它有效,但我建议对最内层循环使用不重叠的变量(例如,%%x,以及%%y)。要删除前导和尾随"",您不需要delims^=^" 选项;只需声明for /f "delims=" %%x in ('echo %%~s') do echo %%~x~ 修饰符删除引号)... 问题更多的是前导和尾随空格而不是“”。 您的意思是前后双引号外部的前导/尾随空格?据我所见,echo %%~s ^| findstr ... 部分可以导致 trailing 空格,可以通过删除(转义)管道之前的空格来避免这种情况,例如echo %%~s^| findstr ...;我看不到 leading 空格的来源,但如果有,您可以通过 for /F "tokens=*" %%l in (...) do echo %%l 删除它们(记住默认分隔符 spacetab i>)... 谢谢,抱歉我的解释可能不正确。如果您查看代码,我必须在输出周围添加“”以使其与 icacls 一起使用,此时有一个前导空格“c:\path\path\executable” 啊,我明白了!中间的for /f循环是前导空格的来源;为避免这种情况,请将其更改为:for /f "tokens=1* delims=: " %%r;我只是交换了tokensdelims 选项,并将 space 定义为最后一个分隔符(只有当它是选项字符串中的最后一个字符时,空格才被用作分隔符);这避免了前导空格并且可以工作,因为第一个标记本身不包含空格... 【参考方案1】:

在引号之间括起来路径是绝对正确的,以避免出现空格问题。 但是,我不会删除也包含在"" 中的前导和尾随空格,而是尝试不生成它们,而不是稍后消除它们。


前导空格来自中间for /f循环:

for /f "delims=: tokens=1*" %%r in ('sc qc "%%~n" ^| findstr BINARY_PATH_NAME') do ( ... )

过滤后的sc qc 命令产生如下输出行:

        BINARY_PATH_NAME   : C:\Windows\System32\spoolsv.exe
        BINARY_PATH_NAME   : "C:\Program Files\iPod\bin\iPodService.exe"

使用您的 "delims=: tokens=1*" 选项,分隔符 (:) 之后的部分包含前导空格。 为了克服这个问题,添加空格作为另一个分隔符,但要确保它是选项字符串中的最后一个字符:"tokens=1* delims=: "


尾部空格来自echo内部循环解析的管道for /f

for /f tokens^=1-2^ delims^=^" %%x in ('echo %%~s ^| findstr /V /I "c:\windows\system32"') do ( ... )

echo %%~s^| 之间的空格也包含在输出中。因此,只需将其删除。 不需要定义选项字符串tokens^=1-2^ delims^=^",因为for /f 循环无论如何都不应该接收任何要解析的引号(由于%%~s 中的~ 修饰符),所以"delims=" 就足够了。因此仅在循环体中使用%%~x。 此外,如果%%~s 为空,我建议写echo(%%~s 而不是echo %%~s 以避免ECHO is on/off. 消息(尽管这永远不会发生)。


另一个问题是sc query state= all 检索到的服务名称也可能包含空格;例如:

SERVICE_NAME: iPod Service

所以对于外部for /f 循环,"tokens=2" 选项不是最优的;在上面的例子中,只会返回iPod。因此将其更正为"tokens=1*" 并忽略forst 令牌。


由于只搜索文字字符串,我将findstr 实例替换为find,除了最后一个,因为find 仅支持单个搜索字符串。 我在剩余的 findstr 实例中添加了另一个搜索字符串,以便同时覆盖还存在目录 C:\Windows\SysWOW64 的 64 位 Windows 系统,我假设您也想过滤掉它。


这里是固定代码:

@echo off
for /f "tokens=1*" %%m in ('sc query state^= all ^| find "SERVICE_NAME"') do (
    for /f "tokens=1* delims=: " %%r in ('sc qc "%%~n" ^| find "BINARY_PATH_NAME"') do (
        for /f "delims=" %%x in ('echo(%%~s^| findstr /L /V /I /C:"%SystemRoot%\System32" /C:"%SystemRoot%\SysWOW64"') do (
            ECHO icacls "%%~x"
        )
    )
)

测试成功后去掉大写的ECHO,以便执行icacls命令行。

【讨论】:

以上是关于FOR 循环空间和处理的主要内容,如果未能解决你的问题,请参考以下文章

在 3d 空间中创建邻居节点的循环

关于空间/时间的 Groovy 集合性能注意事项

这个函数(for循环)空间复杂度是O(1)还是O(n)?

这个程序的空间复杂性是多少?

在循环中声明的变量是不是会使空间复杂度 O(N)?

循环和函数式“批处理”