【问题标题】:Pausing a batch file when double-clicked but not when run from a console window?双击时暂停批处理文件,但从控制台窗口运行时不暂停?
【发布时间】:2010-08-23 21:33:10
【问题描述】:

有没有办法让批处理文件(在这种情况下,在 Windows XP 上运行)确定它是从命令行启动(即在控制台窗口内)还是通过 shell 启动(例如通过双击) ?

我有一个脚本,当通过 shell 运行时,我希望在某些点暂停,但在命令行运行时不这样做。我在 SO 上看到了a similar question,但由于两个原因无法使用相同的解决方案:首先,它是否暂停需要取决于多个因素,其中一个是它是否被双击。其次,我将把这个脚本分发给我团队中的其他人,我不能要求他们所有人都进行注册表更改,这会影响所有脚本。

这可能吗?

【问题讨论】:

  • 另一个答案没有建议更改注册表。仅更改在快捷方式上执行的命令行。当然,如果您双击实际的批处理文件,这对您没有多大帮助。
  • @Joey — 你是对的;我把答案和它的第一条评论混为一谈。 ;-)

标签: windows batch-file


【解决方案1】:

找到一个 :-) – 在拼命思考cmd 在交互式运行时可能会做什么但在直接启动批处理文件时不会……我终于找到了一个。

伪变量%cmdcmdline% 包含用于启动cmd 的命令行。如果cmd 正常启动,它包含类似于以下内容:

"C:\Windows\System32\cmd.exe"

但是,当启动一个批处理文件时,它看起来像这样:

cmd /c ""C:\Users\Me\test.cmd" "

小演示:

@echo off
for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set DOUBLECLICKED=1
if defined DOUBLECLICKED pause

虽然这种检查方式可能不是最可靠的,但 /c 仅应在直接启动批处理文件时作为参数出现。

在 Windows 7 x64 上测试。它可能会也可能不会起作用、坏掉、做一些奇怪的事情、吃掉孩子(可能是件好事)或咬你的鼻子。

【讨论】:

  • 呸。我只是在玩类似的东西,但试图确定%CMDCMDLINE% 是否包含%0。但是,检查/c so 要容易得多,而且我想不出我的方法可行的任何极端情况,但这不会。谢谢!
  • @Ben:我也想到了%0,但遗憾的是它坏了。当我运行%cmdcmdline%for 时,批处理的路径和名称被分成两个标记,因为我的主目录包含空格并且命令行有双双引号,如上所示,这会抛出for 关闭。另一种选择可能是使用echo.%cmdcmdline%|findstr /i /c:"%~0" >nul 2>&1 && set DOUBLECLICKED=1(或者||,我总是用findstr 出错),它实际上会搜索%0,而不是依赖于标记化。不过,如果该批次被另一个批次调用,它仍然会失败。
  • ——我实际上使用的是子字符串技巧而不是FOR,但是批处理并不适合字符串操作。您的 /c 技巧更简单、更容易,除非用户做了真的奇怪的事情,否则应该可以工作。
  • 在我的 Windows 7 副本上,以管理员身份运行批处理文件会使资源管理器通过大写 /C 作为参数。这将进行不区分大小写的比较:for %%x in (%CMDCMDLINE%) do if /I [%%~x] equ [/c] pause
  • 不错,但 IMO 不足,因为即使从 PowerShell 调用也使用 cmd /c
【解决方案2】:

综合答案,源自此页面上的大部分信息(以及其他一些具有类似问题的堆栈溢出页面)。这个不依赖检测/c,而是实际检查命令行中的脚本名称。因此,如果您双击另一批次然后调用该批次,该解决方案将不会暂停;你必须双击这个特定的批处理文件。

:pauseIfDoubleClicked
setlocal enabledelayedexpansion
set testl=%cmdcmdline:"=%
set testr=!testl:%~nx0=!
if not "%testl%" == "%testr%" pause
  1. 变量“testl”获取 cmd 处理器调用的完整行,去掉所有讨厌的双引号。
  2. 变量“testr”采用“testl”并进一步去除当前批处理文件的名称(如果存在)(如果批处理文件是通过双击调用的)。
  3. if 语句查看“testl”和“testr”是否不同。如果是,批处理被双击,所以暂停;如果不是,批处理是在命令行中输入的(或从另一个批处理文件中调用),继续。

编辑:同样可以在一行中完成:

echo %cmdcmdline% | findstr /i /c:"%~nx0" && set standalone=1

用简单的英语,这个

  • %cmdcmdline% 的值通过管道传送到findstr,然后搜索当前脚本名称
  • %0 包含当前脚本名称,当然前提是 shift 没有被事先调用
  • %~nx0%0 中提取文件名和扩展名
  • >NUL 2>&1 通过将任何输出重定向到 NUL 来使 findstr 静音
  • findstr 如果找不到有问题的子字符串,则设置非零错误级别
  • && 仅在前面的命令返回且没有错误时执行
  • 因此,如果脚本是从命令行启动的,standalone 将不会被定义

在脚本的后面我们可以这样做:

if defined standalone pause

【讨论】:

  • 不错,我喜欢单线!这适用于 Windows 7。如果您不使用“shift”,您甚至可以在脚本末尾直接调用 ECHO %cmdcmdline% | findstr /i /c:"%~nx0" >NUL 2>&1 && PAUSE - 不需要变量。
【解决方案3】:

一种方法可能是在 c:\ 的根目录中创建一个 autoexec.nt 文件,该文件类似于:

@set nested=%nested%Z

在您的批处理文件中,检查 %nested% 是否为“Z” - 如果为“Z”,则您已被双击,因此请暂停。如果不是“Z” - 它将是“ZZ”或“ZZZ”等,因为 CMD 继承了父进程的环境块。

-奥辛

【讨论】:

  • 有趣的想法,但似乎行不通。 AUTOEXEC.NT 似乎被完全忽略了,AUTOEXEC.BAT 只执行一次(在系统启动时),并且 AutoRun 注册表项在每个 CMD.EXE 会话中执行一次,无论它是如何启动的。
  • 批处理文件不会在新的cmd 进程中启动。所以如果我只是启动一个cmd 实例并从那里启动批处理文件,nested 将是Z。否则是检测嵌套 shell 的好主意,但不适用于批处理文件。
  • 另一个注意事项:autoexec.nt 从未驻留在驱动器的根目录中,而是驻留在 %systemroot%\system32 中。据我所知,它只影响command.com,但不影响cmd
  • 哦,好吧。有时只是值得发布您的想法,希望它可能会触发其他人的脑电波。
【解决方案4】:

更多信息...

我从一个包含以下内容的批处理文件 (test.cmd) 开始:

@echo %cmdcmdline%

如果我在 Windows 资源管理器中双击“test.cmd”批处理文件,echo %cmdcmdline% 的显示为:

cmd /c ""D:\Path\test.cmd" "

在命令提示符窗口中执行“test.cmd”批处理文件时,
echo %cmdcmdline% 取决于命令窗口的启动方式...

如果我通过单击“Start-Orb”和“命令提示符”来启动“cmd.exe”,或者如果我单击“Start-Orb”并从搜索/运行框中执行“cmd.exe”。然后我执行“test.cmd”批处理文件,echo %cmdcmdline% 的显示是:

"C:\Windows\system32\cmd.exe"

另外,对我来说,如果我从桌面快捷方式中单击“命令提示符”,然后执行“test.cmd”批处理文件,echo %cmdcmdline% 的显示也是:

"C:\Windows\system32\cmd.exe"

但是,如果我在 Windows 资源管理器窗口中“右键单击”并选择“在此处打开命令提示符”,然后执行“test.cmd”批处理文件,则 echo %cmdcmdline% 的显示为:

"C:\Windows\System32\cmd.exe" /k ver

所以,请注意,如果您从“目标”字段中包含“/c”的快捷方式启动“cmd.exe”(不太可能),那么前面示例中的测试将无法测试这种情况正确。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-21
    • 2022-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多