<nul set /p Text Output approach has a weird quirk with Commands and Pipeline

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

TLDR:

set /p
命令 force
CMD interpreter
是否会评估管道,即使它已转义?有可能避免这种情况吗?

进一步阅读

考虑批处理文件中的以下行。这不是我发现的重复问题 - 如果有人问过这个问题,请指出它,我会很乐意阅读和吸收。下面代码的目的是将

Echo F | Xcopy /Y 'FilePath1' 'FilePath2'
输出到日志文件。

这失败了。 。 。有点

<nul (set /p "Lineout=Echo F ^| Xcopy /Y '%~1' '%~2'") >>%log%

我成功地按预期将字符串输出到

%log%
,但在批处理文件的控制台中,每次列出此内容时,我都会收到
Invalid Parameters
警告。我假设问题来自于
|
管道以某种方式调用 Xcopy 命令,但我不明白为什么它会这样做,即使我已经逃脱了它! (我希望 xcopy 运行!)

这有效。 。 .

Echo Echo F ^| Xcopy /Y '%~1' '%~2' >>%log%

如果我

Echo
该行具体如上所示,我在控制台上不会收到任何错误,并且我仍然得到输出,并且 xcopy 不会运行。是否只是因为管道无法逃脱,从而将
CMD
从 STDOUT 中打破并真正调用此命令?

我在一个函数中使用它,其中

args
1 和 2 是文件,但我对字符串使用单引号,因为我知道双引号会破坏
<nul set/p
文本输出方法。

对此有更多疑问。 。 .

是因为我在

set /p
块内执行
()
吗?我已经在双引号内尝试过
|
^|
^^|
,但仍然收到错误,就好像
|
没有被转义一样。当我使用
2>&1
强制将
STDERR
输出到日志文件时,我什至收到错误! (因为我不在乎命令不运行,所以我愿意输出
STDERR
到日志或
nul
- 我稍后在子例程中成功运行它 - 为了清楚起见,这只是日志中的一行关于复制文件时运行的内容)

更奇怪的是 - 对于

|
^|
我在日志输出中看到
^^|
。为什么它会在
^
中的日志中附加两 (2)
set /p
?使用
^^|
,我在日志输出中看到
^^^^|
,但仍然收到
Invalid Parameters
错误!我需要三 (3)
^
才能正确逃脱吗?

我一定错过了一些简单的东西,或者将其归咎于管道固有的其他问题。 。 .

编辑:

如果我使用管道,我是否还应该将命令的输出发送到

()
块内的 nul ,如下所示,还是会被外部重定向到
%log%
所否决?我必须假设无论我做什么,
|
都不会被转义,当我执行
<nul set /p
行来输出
set /p
命令中的2>&1行时,
STDERR
至少会是发送到日志文件或
nul
,具体取决于它的解释方式。 。 .

<nul (set /p "Lineout=Echo F ^| Xcopy /Y '%~1' '%~2' >nul 2>&1") >>%log%

我想假设管道只是执行块内其右侧的命令,并且块外部的重定向正在按照人们对

()

中任何代码块的预期工作
batch-file windows-10 windows-server-2019
1个回答
0
投票

我相信“set /p 命令是否强制 CMD 解释器评估管道,即使它已转义?是否可以避免这种情况?”的答案。是“是的,有条件。”

正如 @Mofi 的评论中提到的,

<nul (set /p "lineOut=Echo F | Xcopy /Y '%~1' '%~2'")>>%log%
的初始使用按预期工作。 。 .

我最初提出问题时没有考虑到(因为我认为这并不重要),当特定的情况下,当它作为另一个例程的另一个参数发送时,字符串

Echo F | XCopy /Y '%~1' '%~2'
是否会以相同的方式解释处理用例。

我假设这两个代码示例是等效的 - 它们不是。

REM Works as expected where '%~1' and '%~2' are file paths enclosed in a double quote line to be sent to a file.
<nul (set /p "lineOut=Echo F | Xcopy /Y '%~1' '%~2'")>>%log%

REM Does NOT work where %* contains the string listed above:
<nul (set /p "lineOut=:RoutineName %*")>>%log%

我第一次测试时认为被认为是等效的用例场景如下所示。直到进行更多测试后,我才将 %* 确定为最大的影响因素。

正如 @Mofi 在另一条评论中所述,使用

%*
会失败,因为
|
(以及其他运算符 -
&<>
)将被直接解释,因为它们被从
" string "
上下文中剥离出来并且不能被解释为字符串。

REM Does NOT work - the CMD Interpreter fails to ignore the pipeline when using this approach.
Call :WriteOut "Echo F | Xcopy /Y '%~1' '%~2'" %log% "Yes"

:WriteOut [Verbiage] [Log File] [To Log?]

REM This is the line which I thought compared Apples to Apples.  It does NOT!
<nul (set /p "lineOut=:WriteOut %*") >>%log%

REM This line worked fine
<nul (set /p "lineOut=%~1")

REM this line also worked fine
if not "%~3"=="" (
  <nul (set /p "lineOut=%~1")>>%log%
)

我发现如果我根据需要使用另一个参数,我可以有效地将其正确输出到日志,没有错误:

REM with Parameter 4, we have success.
Call :WriteOut "Echo F | Xcopy /Y '%~1' '%~2'" %log% "Yes" "Yes"

:WriteOut [Verbiage] [Log File] [To Log?] [Lineout Contains Special Characters?]

If "%~4"=="" (
  <nul (set /p "lineOut=:WriteOut %*") >>%log%
) else (
  >>%log% Echo(:WriteOut %*
)

REM . . . code . . .

我也可以像过去一样这样做(没有意识到这也在我遇到问题之前解决了我的问题)

:WriteOut [Verbiage] [Log File] [To Log?] [Lineout Contains Special Characters?]
REM Break apart Args supplied, and send output safely to log file
For %%a in (%*) do (
  set /a argCnt+=1
  REM Assumes you have !cr! and !lf! defined appropriately previously
  <nul (set /p "lineOut=:WriteOut Arg!argCnt! = %%~a!cr!!lf!")>>%log)
)

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