我正在制作批量API。它的一个功能是colortext,它使用PowerShell(如果机器上不支持PowerShell,则使用findstr
)来完成其工作。 (它只是转换给定的批处理颜色,如3C
,将其转换为PowerShell友好名称,如DarkCyan
和Red
,并使用更多内容打印以彩色显示的文本。)< / p>
但是,当我尝试将前景设置为F
(White
)时,由于某种原因,它会尝试执行2到4次(取决于我是否启用了延迟扩展)。
如果从未应用假定的%fcn%
和%bcn%
,它有时会在上次运行时失败/尝试显示相同的内容。 (这也取决于我是否启用了延迟扩展)。
我只想让它执行一次(并且不会因任何原因而失败),但由于某种原因,它正在执行2到4次。我该怎么做/修复这个程序?
以下是重现此问题所需的最少代码,假设%bcn%
,%fcn%
和%text%
已设置(假设Cyan
为%bcn%
且{ {1}}为White
,%fcn%
为Should not happen
):
%text%
此输出变为:
如果我的颜色有效(如@echo off
set bcn=Cyan
set fcn=White
set text="This should not work"
setlocal EnableDelayedExpansion
set script=write-host %text% -ForegroundColor %fcn% -BackgroundColor %bcn%
if EXIST colortest.ps1 ( del colortest.ps1 )
echo %script% >> colortest.ps1
@echo on
@powershell -executionpolicy remotesigned -file colortest.ps1
@echo off
),则使用相同的文字:
以下是批处理文件的完整代码(将其保存为bapi.bat并尝试在pastebin中运行“bapi colortext”这不应该起作用“BF”):
答案 0 :(得分:3)
无法使用发布的批处理代码和环境变量的值重现问题。
然而,aschipfl在评论中也部分提出了一些建议。
可以使用
删除单个文件if exist colortest.ps1 del colortest.ps1
或
del colortest.ps1 2>nul
第一个命令行首先检查要删除的文件是否存在,并且仅当文件确实存在时才运行命令 DEL 。
第二个命令行运行命令 DEL ,并将写入处理 STDERR 的错误消息重定向到设备 NUL 以禁止它。如果要删除的文件根本不存在,则命令 DEL 会输出错误消息。
在 IF 之后或在 FOR 循环中仅运行一个命令时,实际上没有必要在定义命令块的圆括号中编写此命令行。 FOR 和 IF 旨在执行单个命令并使用(
... )
定义命令块只是一个扩展名Windows命令处理器能够在设计中预期单个命令的情况下运行多个命令。
此处可以仅使用>
代替>>
来创建或覆盖colortest.ps1
。这样就无需在之前单独删除文件。
另请参阅Microsoft文章Using command redirection operators。
在命令行上
echo %script% >> colortest.ps1
环境变量引用%script%
和重定向运算符>>
之间存在空格字符。此空格字符也由 ECHO 命令输出,因此也作为尾随空格写入文件。
这在此无关紧要,但在生成的文件中通常不需要尾随空格。一种解决方案是删除空格字符并使用:
echo %script%>> colortest.ps1
但是如果环境变量script
的字符串以空格和1到9范围内的单个数字结尾结束,则会导致意外行为,因为这是在{{}中执行之前预处理此命令行后产生的。 1}}或1>> colortest.ps1
,...这是将句柄1到9重定向到文件2>> colortest.ps1
而不是打印colortest.ps1
到1
来处理 STDOUT < / strong>最后到文件。
解决方案是首先编写重定向,然后编写命令
9
或使用延迟扩展,如果脚本PowerShell脚本行包含特殊字符,这将更好。
>>colortest.ps1 echo %script%
Windows命令解释程序忽略echo !script!>>colortest.ps1
和文件名>>
之间的空格字符。
环境变量colortest.ps1
可以包含一个文本,该文本需要双引用此参数字符串,以便Windows命令解释程序和PowerShell进行正确处理。空格字符(命令行上的分隔符)和字符text
通常需要将整个参数字符串用双引号括起来,以便完全解释为文字文本。
&()[]{}^=;!'+,`~<>|
另请参阅Why is no string output with 'echo %var%' after using 'set var = text' on command line?上的答案为什么使用set "script=write-host "!text!" -ForegroundColor %fcn% -BackgroundColor %bcn%"
而不仅仅是set "script=script line"
以及为什么在将脚本分配给环境时在脚本行中指定了多少双引号并不重要变量
要通过PowerShell脚本同时输出包含一个或多个双引号的set script=script line
,在将text
字符串写入脚本之前,必须再使用一个"
转义每个双引号字符档案text
。
colortest.ps1
在这个例子中可以看到,已经在@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "bcn=Cyan"
set "fcn=White"
set "text="This should not happen^^!""
set "text=!text:"=""!"
set "script=write-host "!text!" -ForegroundColor %fcn% -BackgroundColor %bcn%"
echo !script!>colortest.ps1
echo on
@powershell -executionpolicy remotesigned -file colortest.ps1
@echo off
endlocal
的定义上启用了延迟扩展,其中一个字符串还包含一个感叹号,另外输出两个双引号作为感叹号的感叹号必须使用两个插入符text
进行转义,以将感叹号作为文字字符分配给环境变量^^
。在启用延迟扩展之前,text
的定义没有必要。
text
有关命令 SETLOCAL 和 ENDLOCAL 的详细信息,请参阅this answer,其中详细说明了这两个命令的工作原理。
答案 1 :(得分:0)
这是因为,由于某些随机原因没有发生在其余部分,调用命令执行了2次,第二次没有设置任何%fcn%和%bcn%参数,当call :label
之类的东西是用过的。
但是,goto label
不会发生这种情况。