我经常使用替代 shell (主要是 jpsoft.com 的 TCC/LE) 和子 shell。我发现这段代码适用于更广泛、更一般的情况(而且不需要 FINDSTR):
@echo off & setlocal
if "%CMDEXTVERSION%"=="" ( echo REQUIRES command extensions & exit /b 1 ) &:: REQUIRES command extensions for %cmdcmdline% and %~$PATH:1 syntax
call :_is_similar_command _FROM_CONSOLE "%COMSPEC%" %cmdcmdline%
if "%_PAUSE_NEEDED%"=="0" ( goto :_START )
if "%_PAUSE_NEEDED%"=="1" ( goto :_START )
set _PAUSE_NEEDED=0
if %_FROM_CONSOLE% equ 0 ( set _PAUSE_NEEDED=1 )
goto :_START
::
:_is_similar_command VARNAME FILENAME1 FILENAME2
:: NOTE: not _is_SAME_command; that would entail parsing PATHEXT and concatenating each EXT for any argument with a NULL extension
setlocal
set _RETVAL=0
:: more than 3 ARGS implies %cmdcmdline% has multiple parts (therefore, NOT direct console execution)
if NOT [%4]==[] ( goto :_is_similar_command_RETURN )
:: deal with NULL extensions (if both NULL, leave alone; otherwise, use the non-NULL extension for both)
set _EXT_2=%~x2
set _EXT_3=%~x3
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_2%"=="" (
call :_is_similar_command _RETVAL "%~2%_EXT_3%" "%~3"
goto :_is_similar_command_RETURN
)
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_3%"=="" (
call :_is_similar_command _RETVAL "%~2" "%~3%_EXT_2%"
goto :_is_similar_command_RETURN
)
::if /i "%~f2"=="%~f3" ( set _RETVAL=1 ) &:: FAILS for shells executed with non-fully qualified paths (eg, subshells called with 'cmd.exe' or 'tcc')
if /i "%~$PATH:2"=="%~$PATH:3" ( set _RETVAL=1 )
:_is_similar_command_RETURN
endlocal & set "%~1=%_RETVAL%"
goto :EOF
::
:_START
if %_FROM_CONSOLE% EQU 1 (
echo EXEC directly from command line
) else (
echo EXEC indirectly [from explorer, dopus, perl system call, cmd /c COMMAND, subshell with switches/ARGS, ...]
)
if %_PAUSE_NEEDED% EQU 1 ( pause )
最初,我在_is_similar_command
子程序中使用了if /i "%~f2"=="%~f3"
。改为使用if /i "%~$PATH:2"=="%~$PATH:3"
并增加检查NULL扩展名的代码使得该代码适用于使用非全限定路径(例如,仅使用'cmd.exe'或'tcc'调用的子shell)打开的shell/subshell。
对于没有扩展名的参数,此代码不会解析和使用%PATHEXT%中的扩展名。当搜索没有扩展名的命令时(首先尝试FOO.com,然后是FOO.exe,然后是FOO.bat等),它基本上忽略了CMD.exe使用的扩展名层次结构。因此,_is_similar_command
检查两个参数之间作为shell命令的相似性而不是等同性。这可能是混淆/错误的源头,但在实践中很可能永远不会成为问题。
编辑:初始代码是旧版本。现在的代码已经更新到最新版本,其中包括:(1) 初始调用中交换了%COMSPEC%
和%cmdcmdline%
,(2) 添加了对多个%cmdcmdline%
参数的检查,(3) 回显消息更具体地指示了检测到的内容,以及 (4) 新增了一个变量%_PAUSE_NEEDED%
。
需要注意的是,%_FROM_CONSOLE%
的设置是基于批处理文件是直接从控制台命令行还是通过资源管理器等其他方式间接执行的。这些“其他方式”可以包括perl的system()函数调用,或者通过执行类似cmd /c COMMAND
的命令来执行。
添加了变量
%_PAUSE_NEEDED%
,以便间接执行批处理文件的进程(如perl)可以绕过批处理文件中的暂停。在输出未传输到可见控制台的情况下(例如
perl -e "$o = qx{COMMAND}"
),这将非常重要。如果在这种情况下发生暂停,则“按任意键继续...”暂停提示永远不会显示给用户,进程将挂起等待未经提示的用户输入。在无法进行用户交互或不允许用户交互的情况下,可以预设
%_PAUSE_NEEDED%
变量为“0”或“1”(分别表示false或true)。代码仍然正确设置
%_FROM_CONSOLE%
,但是
%_PAUSE_NEEDED%
的值不会根据
%_FROM_CONSOLE%
后续设置。它只是通过传递。
还要注意,如果使用带有开关/选项的命令(例如cmd /x
)打开子shell,则该代码将错误地将执行检测为间接的(%_FROM_CONSOLE%
=0)。通常这不是一个大问题,因为子shell通常是在没有额外开关的情况下打开的,当必要时可以将%_PAUSE_NEEDED%
设置为0。
程序员的警告。