我有以下循环:
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 命令运行良好。
我尝试过转义破折号和分号,但没有成功。
导致此错误的原因可能是什么?
因为
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
在技术上就足够了。
powershell -help
即可。在示例中,您会找到
PowerShell -Command "& {Get-EventLog -LogName security}"
行。这就是您正在寻找的。但是还有一些我不明白的地方。
($_.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
(Get-Date $_.TimeGenerated)
部分没有意义,因为
($_.TimeGenerated)
已经是
System.DateTime
$error[0]
是一种非常复杂的数据类型,
%%a %%b %%c
无法捕获。只需在 Powershell 窗口中输入
Some rubbish to get an error
,然后输入
$error[0]
。然后尝试
$error[0] | ConvertTo-Json
了解该对象的复杂性。
Write-Output
?我建议使用
return
,因为它是为向 StdOut 写入内容而设计的,当 Powershell.exe 必须返回内容时也是如此。 Write-Output 可以在 Powershell 内部使用,但是要通过完成 Powershell 进程来写入 StdOut 吗?我不确定,因为我没有尝试过。
@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
)
请原谅我糟糕的英语。