1

我认为这是不可能的,但我希望能够做到这一点,或者可能使用另一种方法......

我有一个批处理文件;

for /f "usebackq tokens=*" %%a in (`wmic process get description, commandline`) do (
*Some Code*
)

我需要能够从每一行中获取两个答案,并单独使用它们(基本上,使用描述来检查进程是否正在运行,然后在我杀死进程并完成一些文件清理工作之后,重新加载包括任何命令行参数的原始进程。

我可能需要结束/重新打开的进程的一个输出示例可能是:

"C:\some folder\some other folder\some_application" -cmd_parameter                process_name.exe

请注意,描述是由多个空格明确定义的。


所以有没有一种说法

for /f "tokens=* delims=  "    <--(The delims is TWO spaces, not space OR space)

另一种可能更好的方法可能是使用特殊字符(即从未在进程或路径中使用的)复制多个空格的所有实例,然后将其用作我的分隔符......虽然我不知道是否这甚至是可能的..

我也对任何替代方法持开放态度,只要我可以获得进程名称(检查预定义的进程列表、exe 的完整路径以及给出的任何命令行参数。

谢谢大家

4

3 回答 3

2

直接回答您的问题:不,您不能指定 2 个空格作为分隔符。您可以使用 SET 搜索和替换将 2 个空格更改为某个唯一字符,但确定一个永远不会出现在您的描述或命令行中的唯一字符说起来容易做起来难。

更好的选择是将 WMIC 的输出格式更改为 LIST - 每行一个值,格式为propertyName=Value. 每个属性值都可以存储在一个变量中,然后当记录了流程的最后一个属性时,您可以使用变量值执行操作。WMIC 输出使用 Unicode,这会导致在每个变量分配的末尾附加一个 CarriageReturn 字符。必须剥离 CarriageReturn 才能获得正确的结果。

@echo off
setlocal
for /f "tokens=1* delims==" %%A in ('"wmic process get description, commandline /format:list"') do (
  if "%%A"=="CommandLine" (
    set "cmd=%%B"
  ) else if "%%A"=="Description" (
    set "desc=%%B"
    setlocal enableDelayedExpansion
    set "desc=!desc:~0,-1!"
    set "cmd=!cmd:~0,-1!"
    echo(
    echo Do whatever you need to do with the description and command line.
    echo description=!desc!
    echo command line=!cmd!
    endlocal
  )
)

您需要注意一些事项。

1) 对于同一个图像名称,您可以有多个进程。如果您通过图像名称(描述)杀死一个进程,那么您将删除所有它们。如果你也根据命令行重启它,那么当下一个同名进程被杀死时,它会再次被杀死。最好通过进程 ID 终止进程。

2)如果您知道进程的图像名称(描述),那么您可以使用 WMIC WHERE 子句限制您的输出。

3) WMIC 报告的命令行并不总是可靠的。该进程能够修改作为命令行报告的值。

这是一个检索特定描述的进程 ID 和命令行的解决方案。
编辑 - 我修复了下面的代码

@echo off
setlocal
for /f "tokens=1* delims==" %%A in ('"wmic process where description='MyApp.exe' get processId, commandline /format:list"') do (
  if "%%A"=="CommandLine" (
    set "cmd=%%B"
  ) else if "%%A"=="ProcessId" (
    set "id=%%B"
    setlocal enableDelayedExpansion
    set "id=!id:~0,-1!"
    set "cmd=!cmd:~0,-1!"
    echo(
    echo Do whatever you need to do with the process id and command line.
    echo process Id=!id!
    echo command line=!cmd!
    endlocal
  )
)

注意 - WMIC WHERE 子句使用 SQL 语法。它可以使用 AND 和 OR 条件使其变得复杂,并且它支持使用 % 和 _ 作为通配符的 LIKE 运算符。我相信当它变得复杂时,整个表达式需要用双引号括起来。

于 2012-08-14T17:23:49.767 回答
1

前言

我只是想为未来的读者添加这个,因为我遇到了这个问题,我自己解决了它,我认为仅仅展示如何简单地做到这一点会很有用。首先,dbenham他的回答是绝对正确的,“不,你不能指定 2 个空格作为分隔符。” . 由于您不能直接使用批处理 for 循环来执行此操作,因此您可以简单地制作自己的来完成这项工作。再次dbenham是正确的说法

“您可以使用 SET 搜索和替换将 2 个空格更改为某个唯一字符”

这与我所做的有点相似(有一些差异),但为了完整起见,我认为将其记录在案是件好事。问题是,简单地将所有出现的双空格设置为其他字符并不总能解决问题。有时我们有两个以上的空格,而我们真正想要的是用多个空格分隔字符串。我试图在这里解决的问题更像这样(来自 OP)Ricky Payne

“另一种可能更好的方法可能是使用特殊字符(即从未在进程或路径中使用的)复制多个空间的所有实例,然后将其用作我的分隔符......虽然我不知道如果这是可能的话..”

答案是这是可能的,而且一点也不难。你所需要的只是能够

A. 遍历字符串的每个字符

B. 区分单空格和双(或更多)空格

C. 遇到双空格时打开标志

D. 将双(或更多)空格转换为可以分隔的特殊字符或字符序列。

编码

为此,我编写了代码供自己使用(为清晰起见进行了编辑):

FOR /F "tokens=* delims=*" %%G IN ('<command with one line output>') DO (SET 
"LineString=%%G")
SET /A "tempindex=0"
:LineStringFOR
    SET "currchar=!LineString:~%tempindex%,1!"
    IF "!currchar!"=="" (goto :LineStringFOREND)
    SET /A "tempindex=!tempindex!+1"
    SET /A "BeforeSpacePosition=!tempindex!"
    SET /A "AfterSpacePosition=!tempindex!+1"
    IF NOT "!LineString:~%BeforeSpacePosition%,2!"=="  " (goto :LineStringFOR)
    :LineStringSUBFOR
        IF "!LineString:~%BeforeSpacePosition%,2!"=="  " (
        SET LineString=!LineString:~0,%BeforeSpacePosition%!!LineString:~%AfterSpacePosition%!
        GOTO :LineStringSUBFOR
        ) ELSE (
        SET LineString=!LineString:~0,%BeforeSpacePosition%!;!LineString:~%AfterSpacePosition%!
        GOTO :LineStringSUBFOREND
        )
    :LineStringSUBFOREND
    GOTO :LineStringFOR
:LineStringFOREND
ECHO Final Result is "!LineString!"

因此,如果您的输入(FOR 中命令的输出,或者您可以更改该 FOR 循环以接收字符串)是:

"abcab c"

输出应采用以下格式:

“a;b;c;ab c”

我已经在我自己的代码上对此进行了测试。但是,对于我在这里的回答,我删除了所有评论并更改了一些变量名称以清楚起见。如果在输入命令后此代码不起作用,请随时告诉我,我会更新它,但它应该可以工作。在此处格式化可能会阻止直接复制粘贴。

只是为了展示实际发生的事情

程序流程基本上是这样的:

FOR each character
:TOP
grab the next character
set a variable to the current index
set another variable to the next index
IF this or the next character are not spaces, goto the TOP
:Check for 2 spaces again
IF this and the next character are both spaces then
    get the string up to (but not including) the current index AS A
    get the string after the current index AS B
    set the string to A+B
    goto Check for 2 spaces again
ELSE we have turned the double or more space into one space
    get the string up to (but not including) the current index AS A
    get the string after the current index AS B
    set the string to A + <char sequence of choice for delimiting> + B
goto TOP to grab the next character
After all characters are looped over
RETURN the string here (or echo it out like I did)

额外的

dbenham在他对这种方法的回答中说:

“您可以使用 SET 搜索和替换将 2 个空格更改为某个唯一字符,但确定一个永远不会出现在您的描述或命令行中的唯一字符说起来容易做起来难。”

虽然这在过去可能是正确的,但我承认(至少对于我的方法,如果我在其他情况下错了,请纠正我)你实际上可以使用一个绝对不会出现在你的输入中的分隔符。这是通过使用多字符分隔符来完成的。这不允许您使用标准的 FOR 循环,但是您可以很容易地手动执行此操作。此处对此进行了更深入的描述:

"delims=#+#" - 超过 1 个字符作为分隔符

于 2018-10-25T15:42:04.973 回答
1

伟大的线程!

这让我开始思考,我想出了一个稍微横向的解决方案,它可能对某人有用,就像对我一样。

由于最初的问题是针对 WMIC 命令的,并且输出可以是 CSV 格式,为什么不通过使用 /format:csv 开关并设置逗号作为分隔符并结合“usebackq”来规避空间处理?

当然,如果来自 WMIC 的数据本身有逗号,但在我只想要 BootOptionOnWatchDog 状态的实例中,这可能不起作用

看起来像这样:

FOR /F "usebackq skip=1 tokens=1-31 delims=," %a IN (`%windir%\system32\wbem\wmic computersystem list /format:csv`) DO echo %f

返回:

BootOptionOnWatchDog
Normal boot

我结束了使用'skip = 2',这将返回“正常启动”

顺便说一句,不要经常在这里发帖,因此作为客人发帖,但认为把这个放在这里是谨慎的,因为正是这篇文章帮助我找到了上面的答案。

干杯-史蒂夫(新西兰)

于 2020-03-21T06:30:19.287 回答