使用wmic时转义字符串

问题描述 投票:3回答:2

我正在尝试使用wmic启动VLC实例我这样做主要是因为我想捕获创建过程的pid

我知道下面的命令在命令行中工作正常:

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"

(其中rtsp:// abcd可以是用于此示例的任何输入文件)

试图通过wmic运行它,以各种不同的尝试作为转义序列(其中下面是一个):

wmic process 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" '

可靠地给了我同样的错误:

Invalid format.
Hint: <assignlist> = <propertyname>=<propertyvalue> [, <assignlist>].

但是以下内容:

wmic process create 'C:\PROGRA~1\VideoLAN\VLC_117\vlc.exe rtsp://abcd --sout="#duplicate{dst=display} --rtsp-caching=120 --no-video-title-show"'

工作正常 - 除了作为命令它对我没用。所以问题似乎是我原始命令的嵌套卷曲括号部分。

我尝试了各种不同的逃脱角色......到目前为止还没有成功。任何人都可以建议我哪里出错了?

cmd wmic
2个回答
9
投票

在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必须在创建为一个连续字符串后才能看到整个命令行。 '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命令。这增加了更多的复杂性。由于FOR / F引入了额外的解析层,因此必须转义像=,等不带引号的CMD令牌分隔符。由于额外的解析层,还必须转义未加引号的毒物字符。

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所以我只是用一个ECHO语句替换CMD / C.尝试使用逗号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,我们一群人开发了various methods for a batch script to determine its own 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

0
投票

我想我可能会在这里发布另外一些观察结果。在使用WMI创建进程时,PowerShell似乎更能抵抗令牌和符号的混淆。只需反击就可以转义嵌套的引号。例:

$addr = "rtsp://abcd"
$cmd = "cmd /k echo C:\PROGRA~1\VideoLAN\VLC_117\vlc.exe $addr --sout=`"#duplicate{dst=display, dst={std{access=file, mux=ps, dst='vlc_117/video.mpg'}}} --rtsp-caching=120 --no-video-title-show`""
$proc = ([wmiclass]'win32_process').create($cmd)

$proc.ProcessId

Windows脚本宿主(JScript和VBScript)同样可以抵抗逗号问题。这是一个Batch + JScript混合脚本(以.bat扩展名保存)来演示:

@if (@CodeSection == @Batch) @then
@echo off & setlocal

set "addr=rtsp://abcd"
for /f %%I in ('cscript /nologo /e:JScript "%~f0" "%addr%"') do set "PID=%%I"
echo Process: %PID%
goto :EOF

@end // end Batch / begin JScript hybrid code
var wmi = GetObject("winmgmts:root\\cimv2"),
    proc = wmi.Get("Win32_Process").Methods_("Create").InParameters.SpawnInstance_();

proc.CommandLine = 'cmd /k echo C:\\PROGRA~1\\VideoLAN\\VLC_117\\vlc.exe '
    + WSH.Arguments(0)
    + ' --sout="#duplicate{dst=display, dst={std{access=file, mux=ps, dst=\'vlc_117/video.mpg\'}}} --rtsp-caching=120 --no-video-title-show"';

var exec = wmi.ExecMethod('Win32_Process', 'Create', proc);
WSH.Echo(exec.ProcessId);

对于PowerShell和JScript解决方案,请删除cmd /k echo以将vlc命令投入生产。


RE:纯批处理,我不能为问题中引用的罕见复杂的vlc命令提供太多,但这可能对其他人有所帮助。对于需要嵌入式引号但不必处理逗号的不太复杂的命令,可以通过使用反斜杠转义它们来在wmic命令中嵌入引号。

wmic process call create "powershell \"test-connection localhost -count 1\""

如果这在您的情况下不起作用,您还可以尝试将整个值放入通过延迟扩展检索的变量中。例:

set "proc=powershell \"test-connection localhost -count 1\""
setlocal enabledelayedexpansion
for /f "tokens=2 delims==;" %%I in (
    '2^>NUL wmic process call create "!proc!" ^| find "ProcessId"'
) do endlocal & set /a PID = %%I
echo Proc: %PID%

或者为方便重用,您可以将其放入子例程:

@echo off & setlocal

set "pc=localhost"
call :run PID "powershell -window normal \"test-connection %pc% -count 1\""
echo Proc: %PID%
goto :EOF

:run <varname_to_return_PID> <command>
setlocal
set proc=%*
call set proc=%%proc:%1 =%%
setlocal enabledelayedexpansion
for /f "tokens=2 delims==;" %%I in (
    '2^>NUL wmic process call create !proc! ^| find "ProcessId"'
) do (endlocal & endlocal & set /a %~1 = %%I && goto :EOF)

不幸的是,这些纯批处理解决方案都没有解决逗号问题。

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