如果批处理脚本是从Windows管理器中启动的,有没有一种方法可以保持控制台的打开状态?

11

我有一个DOS批处理脚本,它会调用一个与用户通过控制台UI交互的Java应用程序。为了举例,我们称之为 runapp.bat,它的内容如下:

java com.example.myApp
如果批处理脚本在控制台中调用,一切都正常。但是,如果从窗口管理器中调用脚本,则新打开的控制台会在应用程序执行完毕后立即关闭。我希望无论何种情况下都能让控制台保持打开状态。
我知道以下技巧:
- 在脚本末尾添加一个`pause`命令。如果从命令行调用`runapp.bat`,这有点丑陋。 - 使用`cmd /K java com.example.myApp`创建一个新的Shell。这是我迄今为止发现的最好的解决方法,但从命令行调用时会留下额外的Shell环境,因此调用`exit`实际上并不会关闭Shell。
是否有更好的方法?
6个回答

12

请看这个问题:检测批处理文件如何被执行

如果从命令控制台运行此脚本,它将不会暂停,但是如果在资源管理器中双击运行,它将会暂停:

@echo off
setlocal enableextensions

set SCRIPT=%0
set DQUOTE="

:: Detect how script was launched
@echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1

:: Run your app
java com.example.myApp

:EXIT
if defined PAUSE_ON_CLOSE pause

3

我更喜欢使用%cmdcmdline%,正如在另一个问题的评论中Patrick的回答中所提到的(尽管我没有找到该问题)。这样,即使有人决定使用引号调用批处理脚本,也不会触发误报。

我的最终解决方案:

@echo off
java com.example.myApp %1 %2

REM "%SystemRoot%\system32.cmd.exe"   when from console
REM cmd /c ""[d:\path\script.bat]" "  when from windows explorer

@echo %cmdcmdline% | findstr /l "\"\"" >NUL
if %ERRORLEVEL% EQU 0 pause

3
cmd /K java com.example.myApp & pause & exit

会完成任务。 & 将一个接一个地执行命令。如果使用 &&,则可以在一个失败时中断。


1

在批处理文件中包含此行,并在资源管理器中双击批处理文件:

cmd /k "在这些引号内的脚本命令由 && 分隔"

例如

cmd /k "cd ../.. && dir && cd some_directory"

可以在这里找到有关 cmd 的全部选项。


0

我经常使用替代 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。

程序员的警告。


-2

@echo %CMDCMDLINE% | find /I " /c " >nul && pause

的程序代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接