引用和转义规则在CMD中已经足够复杂了,当你添加WMIC的复杂性后,它们甚至变得更加疯狂。
WMIC通常可以使用单引号
'
或双引号
"
作为引号字符。在双引号内部的双引号可以使用
\"
进行转义。在单引号内部的单引号也可以使用
\'
进行转义,但反斜杠似乎没有被消耗,所以它似乎是无用的。
CMD只使用双引号
"
,而且没有办法在带引号的字符串内转义
"
。不在引号内的毒瘤字符,例如
&
、
|
、
<
等,必须像
^&
这样进行转义。
尝试合并WMIC和CMD的引用和转义规则是棘手的。请记住,CMD的引用和转义是在WMIC看到命令行之前发生的。
此外,您可以使用
PROCESS CALL CREATE
而不是
PROCESS CREATE
。我想用
PROCESS CREATE
也可能做到,但我从未见过如何实现。
我不确定您的实际命令行是否可以在不使用WMIC的情况下运行。基于您的代码,我认为以下命令可以工作:
C:\PROGRA~1\VideoLAN\VLC_117\vlc.exe rtsp://abcd --sout="#duplicate{dst=display, dst={std{access=file, mux=ps, dst='vlc_117/video.mpg'}}} --rtsp-caching=120 --no-video-title-show"
如果是这样,我相信下面的WMIC命令将起作用:
wmic process call create 'C:\PROGRA~1\VideoLAN\VLC_117\vlc.exe rtsp://abcd --sout="#duplicate{dst=display, dst={std{access=file, mux=ps, dst='vlc_117/video.mpg'}}} --rtsp-caching=120 --no-video-title-show"'
WMIC必须将create后的整个命令行视为一个连续字符串。由于内容中没有空格,
'vlc_117/video.mpg'
中的单引号不会造成问题。添加空格将破坏此解决方案,需要另一种策略。
如果在exe路径周围使用双引号,则应该能够使用长路径而不是短路径:
wmic process call create '"C:\Program Files\VideoLAN\VLC_117\vlc.exe" rtsp://abcd --sout="#duplicate{dst=display, dst={std{access=file, mux=ps, dst='vlc_117/video.mpg'}}} --rtsp-caching=120 --no-video-title-show"'
你可能想在批处理文件中将PID捕获到一个变量中,这时需要使用FOR /F命令。这会增加更多的复杂性。未引用的CMD标记分隔符(如
=
,
,
等)必须进行转义,因为FOR /F引入了额外的解析层。未引用的毒瘤字符也必须进行转义,因为有额外的解析层。
for /f "tokens=2 delims=;= " %%N in (
'wmic process call create '"C:\Program Files\VideoLAN\VLC_117\vlc.exe" rtsp://abcd --sout^="#duplicate{dst=display, dst={std{access=file, mux=ps, dst='vlc_117/video.mpg'}}} --rtsp-caching=120 --no-video-title-show"' ^| find "ProcessId"'
) do set "pid=%%N"
在FOR /F IN()子句中的包含在单引号中的内容不会导致问题,因为它们在WMIC看到命令之前就被剥离了。但是,在--sout=...中的未引用的=必须进行转义。
我无法测试以上内容中的任何部分,因此可能没有一个可行的解决方案。然而,我所讨论的概念应该仍然是有效的。
由于引用和转义的多个层面和复杂性,对命令行的看似微小的更改可能会对解决方案产生重大影响。如果命令不是我所理解的那样,请通过编辑您的问题告诉我正确的命令,我可以尝试适应答案。
更新于2014-05-27
很不幸,PROCESS CALL CREATE选项期望第一个字符串参数是完整的命令行,可选地跟随第二个参数——工作目录。坏消息是,这些参数由逗号分隔。你的命令行也有逗号,而PROCESS CALL CREATE解析器(无论它是什么)似乎不支持命令行中的逗号 :(
我尝试过用单引号或双引号引用逗号,但这并没有帮助。我还尝试过用\
转义逗号。同样,没有成功。我还谷歌了一下解决方案,但没有结果。
恐怕除非有消除逗号的VLC语法可用,或者有一种将复杂的VLC命令行参数放入某种外部脚本文件的方法,否则可能没有解决方案。或者也许有其他聪明人发现了逃避逗号的方法?
以下是一些示例,展示了我尝试过的内容。我没有VLC,所以我只用CMD /C替换了ECHO语句。每次尝试ECHO逗号都失败了。
@echo off
:: All of these commands without commas work just fine
wmic process call create 'cmd /c echo hello world^&pause'
wmic process call create 'cmd /c "echo hello world&pause"'
wmic process call create "cmd /c echo hello world&pause"
:: But none of these commands with commas work
wmic process call create 'cmd /c echo hello,goodbye^&pause'
wmic process call create 'cmd /c "echo hello,goodbye&pause"'
wmic process call create "cmd /c echo hello,goodbye&pause"
wmic process call create 'cmd /c echo hello\,goodbye^&pause'
wmic process call create 'cmd /c "echo hello\,goodbye&pause"'
wmic process call create "cmd /c echo hello\,goodbye&pause"
更新于2014年12月23日:已经找到解决方案!
在DosTips网站上,我们开发了各种方法来确定批处理脚本自身的PID。一旦知道了这一点,就可以使用WMIC列出父进程会话的所有子进程。通过仔细的记账,可以可靠地确定每个新创建的子进程的PID。但是需要编写大量代码。只有当您的命令行无法通过WMIC PROCESS CALL CREATE(逗号问题)传递时,才需要使用此方法。
@echo off
setlocal enableDelayedExpansion
:: Get the PID and UID for this batch process
call :getMyPID
:: Initialize an existing PIDs list
set "PIDs= "
:: Get a list of all existing child processes, except for the
:: child CMD.EXE process that was created by this FOR /F loop.
:: This is necessary because the console session that this script
:: is running in might have previously created a child process.
for /f %%A in (
'2^>nul wmic process where "ParentProcessID=%myPID% and not CommandLine like '%%<%UID%>%%'" get ProcessID'
) do for %%B in (%%A) do set "PIDs=!PIDs!%%B "
:: Create your new process as you normally would.
:: For this demonstration, I will simply create a new CMD.EXE session
start
:: Get the PID of the newly created child process by getting all child PIDs,
:: except for the child CMD.EXE process that was created by this FOR /F loop.
:: Ignore any PID that already exists in the %PIDs% list. The only PID left
:: is your new process.
for /f %%A in (
'2^>nul wmic process where "ParentProcessID=%myPID% and not CommandLine like '%%<%UID%>%%'" get ProcessID'
) do for %%B in (%%A) do if "!PIDs: %%B =!" equ "!PIDs!" set "PID=%%B"
:: At this point you could append the new PID to the PIDs list using
:: set "PIDs=!PIDs!%PID% "
:: Then you can repeat the steps of creating a new proces and getting the new PID
::
:: But instead, I will simply show the result and exit
echo new PID=%PID%
exit /b
:getMyPID
setlocal disableDelayedExpansion
:getLock
:: Establish a nearly unique temp file name using %time%
set "lock=%temp%\%~nx0.%time::=.%.lock"
:: Transform the full path into a UID that can be used in a WMIC query
set "uid=%lock:\=:b%"
set "uid=%uid:,=:c%"
set "uid=%uid:'=:q%"
set "uid=%uid:_=:u%"
setlocal enableDelayedExpansion
set "uid=!uid:%%=:p!"
endlocal & set "uid=%uid%"
:: Establish an exclusive lock on the temp file
:: If this fails, then loop back and try again until success
:: This guaranees no two process will ever have the same UID
2>nul ( 9>"%lock%" (
%= The FOR /F loops creates a child CMD.EXE process which in turn invokes WMIC. =%
%= The child CMD.EXE process contains the WMIC query with the UID in its command line. =%
%= The WMIC query identifies the CMD.EXE process with the UID in the command line, =%
%= and returns the parent process ID, which happens to be the PID for this batch script =%
for /f "skip=1" %%A in (
'wmic process where "name='cmd.exe' and CommandLine like '%%<%uid%>%%'" get ParentProcessID'
) do for %%B in (%%A) do set "PID=%%B"
(call ) %= Guarantee success so we don't accidently loop again =%
))||goto :getLock
:: Delete the temp file
del "%lock%" 2>nul
:: Return the result
( endlocal
set "myPID=%PID%"
set "UID=%uid%"
)
exit /b
C:\PROGRA~1\VideoLAN\VLC_117\vlc.exe rtsp://abcd --sout="#duplicate{dst=display} --rtsp-caching=120 --no-video-title-show"
可以工作。但这样做我已经停止了转储到文件。 - user1447903