为什么文件的存在会影响批处理脚本中文件路径的解析方式?

时间:2019-05-04 00:50:43

标签: batch-file

for和命令一起使用时,对于批处理/f命令的工作方式,我有些不了解。

我试图循环执行一个可执行文件。可执行文件原来不存在,这很好。

但是该脚本似乎没有自发地错误地标记字符串,而没有得到有关错误路径的预期错误。这让我想到了一个错误,认为我格式化了for循环和/或错误地使用了引号或反引号。

在该位置放置可执行文件可解决此问题,使其看起来像路径字符串标记化取决于该路径上实际文件的存在。

为什么下面的批处理文件

@echo off
for /f "usebackq delims=" %%i in ( `"C:\Program Files\BOGUS_PATH\FAKE.exe"`) do (
    rem
)

输出'C:\Program' is not recognized as an internal or external command, operable program or batch file.而不是The system cannot find the path specified.吗?

3 个答案:

答案 0 :(得分:4)

首先,不需要我的宠儿,usebackq。功能更简单的等效命令是

@echo off
for /f "delims=" %%i in ('"C:\Program Files\BOGUS_PATH\FAKE.exe"') do (
    rem
)

唯一需要usebackq的时间是在您尝试读取文件时,并且文件路径包含令牌定界符(例如空格)。

for /f "usebackq" %%A in ('"some file.txt"')

所有其他情况都可以使用“正常”表格

for /f %%A in (fileName) do...
for /f %%A in ("string") do...
for /f %%A in ('someCommand') do...

现在回答您的实际问题:-)

文件的存在或不存在实际上不会改变分析。

首先,当您尝试从命令行执行不存在的程序时,您需要了解可能的错误消息。有三种可能性:

1)如果“命令”在第二个位置以外的任何地方都包含冒号,那么您可能会得到以下信息

c:\test\>abc:fake.exe
The filename, directory name, or volume label syntax is incorrect.

c:\test\>abc:\test\fake.exe
The filename, directory name, or volume label syntax is incorrect.

有时,您可能会遇到下一个错误。关于收到消息的规则对我来说并不明显,而且我认为找出确切的规则还不够重要。

2)如果“命令”中包含表示路径的反斜杠,并且该路径无效,那么您将得到

c:\test\>c:bogus\fake.exe
The system cannot find the path specified.

c:\test\>\bogus\fake.exe
The system cannot find the path specified.

c:\test\>abc:bogus\fake.exe
The system cannot find the path specified.

3)如果找不到可执行文件,并且“命令”不包含路径信息或所提供的路径有效,那么您将得到

C:\test\>fake.exe
'fake.exe' is not recognized as an internal or external command,
operable program or batch file.

C:\test>c:\test\fake.exe
'c:\test\fake.exe' is not recognized as an internal or external command,
operable program or batch file.

最后一个难题是为什么"C:\Program Files\BOGUS_PATH\FAKE.exe"给出错误3)而不是2)

如果您在命令行中使用引号执行,则会得到预期的结果:

C:\test>"C:\Program Files\BOGUS_PATH\FAKE.exe"
The system cannot find the path specified.

如果您在命令行中不加引号执行,则会得到以下预期结果:

C:\test>C:\Program Files\BOGUS_PATH\FAKE.exe
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

您的脚本中包含引号,因此您希望使用前引号,但要获得后者。

有两部分可以理解这一点。

首先,FOR / F通过执行来执行命令

C:\WINDOWS\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe"

您可能会认为您的“命令”已加引号,但cmd.exe玩带引号的游戏,这是解释的第二部分。帮助中介绍了cmd.exe引用规则:

c:\test\>help cmd
Starts a new instance of the Windows command interpreter
...
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.

最后一个条件满足1.下的大多数条件 -字符串未指向有效的可执行文件。因此使用了2.规则,因此命令变为

C:\Program Files\BOGUS_PATH\FAKE.exe

现在结果很合理了-“命令”在空格处中断。

'C:\Program' is not recognized as an internal or external command,
operable program or batch file.

如果尝试使用FOR /F执行命令,并且该命令必须用引号引起来,则必须在整个命令周围加上一组引号。

for /f "delims=" %%A in ('""c:\some path\file.exe" "arg 1" arg2"') do ...

但是,如果路径或带引号的参数中包含毒害字符,则上述内容会出现问题,因为多余的引号会弄乱引号。您可以逃脱诸如此类的有毒字符

for /f "delims=" %%A in ('""c:\some path\file.exe" "this^&that" arg2"') do ...

但是我觉得很尴尬,因为您在命令行上使用的命令与FOR /F中使用的命令不匹配。我宁愿转义多余的一组引号,以便命令行上的所有操作也可以在FOR /F中使用。

for /f "delims=" %%A in ('^""c:\some path\file.exe" "this&that" arg2^"') do ...

答案 1 :(得分:3)

FOR 是Windows命令处理器cmd.exe的内部命令。使用带有选项/F的命令 FOR 和要执行的命令,结果以%ComSpec% /c和指定的命令行作为进一步的参数开始,并在其中运行另外一个命令进程背景。

处理 STDOUT 的所有输出均由执行命令 FOR 的批处理文件的当前命令进程捕获,并在终止后由 FOR 处理另外{@ {1}}一行一行地开始。

用于处理另外启动的命令过程的 STDERR 的输出被重定向为处理当前命令过程的 STDERR ,导致在情况下将其显示在控制台窗口中STDERR 既不在额外启动的命令过程中,也不在当前命令过程中重定向到其他句柄,文件或设备 NUL

使用免费的Sysinternals(Microsoft)工具Process Monitor可以看到这一点。

  • 下载包含 Process Monitor 的ZIP文件。
  • 将ZIP文件提取到任何本地目录中。
  • 以管理员身份运行开始cmd.exe,这是运行 Process Monitor 所必需的。
  • 在首先显示的 Process Monitor Filter 对话框中添加条目 Process Name是cmd.exe ,然后使用按钮 OK关闭对话框
  • 切换工具栏上的最后五个选项,但带有工具提示 Show File System Activity 的填充柜符号除外。
  • Ctrl + X 清除列表。
  • 运行批处理文件。
  • 切换回 Process Monitor ,然后按 Ctrl + E 停止捕获。
  • 确保在进程名称列中同时在 PID 列中看到。右键单击列表标题,然后右键单击上下文菜单项选择列... (如果尚未显示 PID 列),请选中进程ID 并点击确定关闭对话框。我建议使用列标题 PID 的拖放操作将列 PID 移至列 Process Name

向上滚动到捕获的文件系统活动列表的开头,然后查找其中显示具有与第一个进程标识符不同的进程标识符的Procmon.exe的行。具有不同PID的第二个cmd.exe是由 FOR 启动的Windows命令处理器实例。

右键单击第二个cmd.exe,在属性的上下文菜单中单击鼠标左键,选择选项卡 Process ,然后查看命令行 >显示:

cmd.exe

好的。因此,我们现在知道 FOR C:\Windows\system32\cmd.exe /c "C:\Program Files\BOGUS_PATH\FAKE.exe" 在运行命令以捕获该命令的输出时分别做了什么。关闭属性窗口。

查看列表,可以看到第二cmd.exe进行了哪些文件系统访问,以找到在指定目录和现有目录中不存在的指定可执行文件。接下来,它也搜索cmd.exe,也没有结果文件。

第一个参数字符串现在在参数分隔符上拆分,该分隔符属于空格字符,例如逗号或等号。因此,第一个参数字符串C:\Program Files\BOGUS_PATH\FAKE.exe.*再次被参数分隔,导致现在将C:\Program Files\BOGUS_PATH\FAKE.exe解释为第一个参数字符串。

C:\Program检查目录路径cmd.exe之前是否存在现在仅C:\的目录路径。

接下来检查是否存在与模式C:\Program Files\BOGUS_PATH匹配的文件,该文件不返回任何此类文件。然后C:\Program.*检查是否存在文件cmd.exe,该文件又没有该文件。

第二秒钟C:\Program放弃寻找可以执行的文件并输出错误消息。

由于空间不足,无法按要求用双引号将完整路径指定为可执行文件确实令人困惑

  

“ C:\ Program Files \ BOGUS_PATH \ FAKE.exe”未被识别为内部或外部命令
  可操作的程序或批处理文件。

这是预期的,但输出是错误消息:

  

'C:\ Program'不被识别为内部或外部命令
  可操作的程序或批处理文件。

我认为,例如,如果用户使用命令(参数)运行cmd.exe,则使用参数分隔符再次解析第一个参数字符串(如果找不到要执行的文件)

cmd.exe

代替

"C:\Programs\MyApplication.exe C:\Temp\FileToProcess.txt"

所以问题是:

这个自动参数解析好还是不好?

嗯,这很难回答。如dbenhamhis answer中所述,它至少以某种方式在"C:\Programs\MyApplication.exe" "C:\Temp\FileToProcess.txt" 的帮助下得到了记录。

答案 2 :(得分:0)

您可能需要做的是将可执行命令用双引号引起来:

@Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe""'
)Do Echo(%%A
Pause

其中双引号的内部集合用于保护路径中的已知空间,而在将内容传递给cmd.exe实例时,外部集合用于保护内部的双引号,其中括号命令是

然后,为了防止命令出现任何错误消息,您应该将STDERR的输出重定向到NUL

@Echo Off
For /F Delims^=^ EOL^= %%A In ('""C:\Program Files\BOGUS_PATH\FAKE.exe" 2>Nul"'
)Do Echo(%%A
Pause

如果该可执行文件不存在,这将阻止错误消息The system cannot find the path specified.,而Do命令将不运行,(因为没有STDOUT要处理)