批处理循环内的powershell oneliner

问题描述 投票:0回答:2

我有以下循环:

for /f "tokens=1-3 delims=- " %%a in ('
 powershell.exe -Command "Get-EventLog -ErrorAction SilentlyContinue -Newest 1 -LogName System -EntryType Error -Source Tcpip | ForEach-Object { \"$(Get-Date $_.TimeGenerated) - $($_.ReplacementStrings -join '#')\" }; Write-output ^$error[0]"
 ') do (
    echo %%a %%b %%c
)

但收到此错误消息:

- was unexpected at this time.

在命令行上,powershell 命令运行良好。

我尝试过转义破折号和分号,但没有成功。

导致此错误的原因可能是什么?

powershell batch-file syntax escaping quoting
2个回答
0
投票

因为

cmd.exe
将嵌套的
\"
引号之间的内容视为 unquoted,[1],因此您必须
^
-转义那里的所有
cmd.exe
元字符,在这种情况下 - 由于命令执行在
for /f
循环内 - 另外 表示所有
(
)
实例:[2]

for /f "tokens=1-3 delims=- " %%a in ('
  powershell.exe -NoProfile -Command "Get-EventLog -ErrorAction SilentlyContinue -Newest 1 -LogName System -EntryType Error -Source Tcpip | ForEach-Object { \"$^($_.TimeGenerated^) - $^($_.ReplacementStrings -join '#'^)\" }; '' + $error[0]"
') do (
  echo %%a %%b %%c
)

对原始命令也进行了一些更改:

    添加了
  • -NoProfile
    来抑制 profile 加载,这使得执行环境更加可预测,并且可以加快命令速度。

  • 多余的

    Get-Date
    已从
    Get-Date $_.TimeGenerated

    中删除
  • Write-Output ^$error[0]
    替换为
    '' + $error[0]
    只打印错误消息,没有杂散的
    ^
    ,这是没有必要的。
    (使用
    ''
    作为
    +
    操作的 LHS,强制 RHS 的 stringification,以便将错误 object 转换为其 消息文本;这相当于
    "$($error[0])"
    ,这将需要额外的
    ^
    -转义)。


[1]

cmd.exe
没有 转义
"
字符的概念,因此
"
\"
同样被视为具有 syntropic 功能的引用字符。因此,
cmd.exe
" \"A|B\" "
之类的东西视为三个标记:双引号标记
" \"
,后跟未加引号的
A|B\
,然后是双引号
" "
。然后,未引用部分中的
|
被视为通常的管道运算符,并使用此类字符串中断命令 - 除非
|
^
转义的。
尝试
echo " \"A|B\" "
echo " \"A^|B\" "

[2] 正如 Compo 指出的那样,严格来说,只有 右括号 - )

 - 才是问题所在并且需要转义,而不是 
(
。当然,考虑到整体 
'...' 外壳,您可能会认为
 都不是问题,但这只是 
cmd.exe
的众多解析“怪癖”之一。为了简单起见,我还将 左括号转义为 ^(
,因为规则“also 
^
 - 转义未加引号的 
(
)
 内的 
for /f
”比是否是 
just 更容易记住左括号或右括号就是问题所在。 一个最小的例子:

for /f %%a in (' echo (hi) ') do echo %%a
打破

for /f %%a in (' echo ^(hi^) ') do echo %%a
有效;

for /f %%a in (' echo (hi^) ') do echo %%a
在技术上就足够了。


0
投票
问题是你如何启动Powershell。只需在 CMD 窗口上运行

powershell -help

 即可。在示例中,您会找到 
PowerShell -Command "& {Get-EventLog -LogName security}"
 行。这就是您正在寻找的。

但是还有一些我不明白的地方。

  1. ($_.ReplacementStrings -join '#')
     减去 
    (Get-Date $_.TimeGenerated)
     不起作用,因为它们的日期类型不兼容。只需在 Powershell 窗口中输入此内容...

    ((Get-EventLog -LogName System -Newest 1).ReplacementStrings -join '#').GetType().FullName
    
    => 
    System.String
    
    
    (Get-EventLog -LogName System -Newest 1).TimeGenerated.GetType().FullName
    
    => 
    System.DateTime
    
    
  2. (Get-Date $_.TimeGenerated)
    部分没有意义,因为
    ($_.TimeGenerated)
    已经是
    System.DateTime
    
    
  3. 返回值
  4. $error[0]
    是一种非常复杂的数据类型,
    %%a %%b %%c
    无法捕获。只需在 Powershell 窗口中输入 
    Some rubbish to get an error
    ,然后输入 
    $error[0]
    。然后尝试 
    $error[0] | ConvertTo-Json
     了解该对象的复杂性。
  5. 为什么使用
  6. Write-Output
    ?我建议使用 
    return
    ,因为它是为向 StdOut 写入内容而设计的,当 Powershell.exe 必须返回内容时也是如此。 Write-Output 可以在 Powershell 内部使用,但是要通过完成 Powershell 进程来写入 StdOut 吗?我不确定,因为我没有尝试过。
最后一个简单的例子,它在我的 Windows 10 和 Powershell 5.1 上运行良好。注意,for循环的分隔符已修改为“#”。

@echo off for /f "tokens=1-3 delims=#" %%a in (' powershell.exe -NoProfile -NoLogo -Command "& {Get-EventLog -LogName "System" -Newest 1 -InstanceId 7001 -Source "Microsoft-Windows-Winlogon" | ForEach-Object { $OutValue = (($PSitem.Source) -replace '-', '#')}; return $OutValue }" ') do ( echo A: %%a echo B: %%b echo C: %%c )
请原谅我糟糕的英语。

© www.soinside.com 2019 - 2024. All rights reserved.