首页 > 系统相关 >10 Windows批处理之调用例程和bat文件

10 Windows批处理之调用例程和bat文件

时间:2024-09-13 10:35:18浏览次数:10  
标签:10 bat 调用 goto 文件 例程 命令

在前文中,我介绍了标签和非顺序执行,这两者在本文中也起着重要作用。我将很快介绍一个已经讨论过的命令的新变化,允许您创建和调用由标签定义的例程。不是简单地在标签之后将控制权交给代码,而是在例程执行后将控制权返回到调用它的位置。在编写更复杂、更有趣的bat文件时,您需要完全理解例程。

在前面文章中,我介绍了调用用其他语言编译的可执行文件的概念。我将在这里展开讨论,描述一个bat文件调用另一个bat文件的不同技术。显然,您将了解最典型的调用类型,它将控制权返回给调用的bat文件。但是,您还将学习放弃对所调用的bat文件的控制的技术,以及如何生成第二个并行批处理进程。此外,您还将探索从例程或bat文件优雅退出的不同方法,无论是否使用返回代码。

call 命令及重新访问

在创建可调用的内部例程之前,必须了解使用标签的两个命令之间的异同。其中之一是call命令,在那里我们使用它来调用用其他语言编译的程序。另一个是goto命令,用于更改bat文件的执行流程。

为了比较和对比这两个命令,请回顾之前介绍的代码:

> con echo Before GOTO
goto :MyLabel
> con echo After GOTO
:MyLabel
> con echo After LABEL

goto 命令跳过了中间的 echo 命令,导致如下输出:

Before GOTO
After LABEL

为了演示对比,下面更改了代码中要调用的每个goto实例,包括goto命令和echo命令中的文本,而在这个非常简洁的bat文件中保留了其他所有内容。

> con echo Before CALL
call :MyLabel
> con echo After CALL
:MyLabel
> con echo After LABEL

执行上面的bat文件,您将看到四行代码写入控制台,而不是某些人可能期望的三行代码。

Before CALL
After LABEL
After CALL
After LABEL

Before CALL 的显示显然是立即执行的。call 命令临时将控制权交给标签后面的代码,导致显示 After LABEL。当这是一个 goto 命令时,此时 bat 文件在显示之后结束。但是使用call命令,在 :MyLabel 和 bat 文件末尾之间的所有内容执行之后,控制立即返回到call命令之后的命令。因此,显示 After CALL。

有些人可能期望执行在此时完成,但解释器接下来再次遇到 :MyLabel。我们不会调用它;相反,它只是一行代码。注意,我没有称它为命令,甚至也没有称它为语句。它只是一行代码,一个占位符,在这个上下文中,只不过是通往下一个命令的路径上的一个非常微妙的减速带。解释器移到bat文件的最后一行,第二次显示文本 After LABEL。解释器找不到其他需要解释的命令,bat文件就完成了。

当 goto 命令放弃控制时,call 命令记住它从哪里来,并在它的业务完成后返回到那个位置。现在我们有了一个可调用的内部例程,我们将用 call 命令调用这个例程。

调用内部例程

随着批处理代码变得越来越有趣,您可能希望从bat文件中的不同位置多次执行一段代码。例如,您可能希望多次调用可执行文件,或者您可能希望定期检查目录中是否有需要复制的文件。当我们用到交互式批处理时,你可能想要问用户一个问题并多次得到响应。

面对对一段代码进行多次调用的需求,新手程序员可能会采用剪切和粘贴的方式——在我极其挑剔的观点中,这是一种令人讨厌的选择。一个更好的解决方案是创建一个内部例程,并从多个位置调用它。您甚至可以将一些只调用一次的代码放入例程中,以便更好地组织您的bat文件。有时直接运行一个标签是完全可以的,但更多时候,您需要创建一个只能通过调用它来调用的例程。

对于下面的练习,我们继续使用上面的代码,以便标签定义一个可调用例程。也就是说,执行流将调用例程,从中返回,并在再次进入该例程之前退出bat文件。为此,我需要一种方法来终止例程和bat文件。即 After LABEL的最终将不再显示。相反,我们期望有这三行输出:

Before CALL
After LABEL
After CALL

下面的代码,看起来有点不同,正是这样做的:

> con echo Before CALL
call :MyLabel
> con echo After CALL
goto :eof & rem End of TestCall.bat

:MyLabel
> con echo After LABEL
goto :eof & rem End of :MyBabel

:AnotherLabel
> con echo This is Never Executed
goto :eof & rem End of :AnotherLabel

在逐步执行代码之前,请注意三个goto :eof命令。如您所料,第一个跳转到文件的末尾,停止bat文件。另外两种说法完全不同,是新出现的。

在初始的 echo 命令之后,call 命令会调用MyLabel的例程,该例程只包含两个命令。第一个是我们熟悉的 After LABEL 回显到控制台,第二个是 goto :eof 命令。因为这个命令是在标签被调用之后执行的,所以它结束的不是文件而是例程,并且控制在调用命令之后返回到命令,在控制台写入 After CALL。最后,主 goto :eof 命令退出bat文件,因为解释器知道它不在例程中。

:MyLabel例程中,转到:eof(或文件结束)是不恰当的;它实际上更像是例行公事的结束,但我们不要在语义上吹毛求疵。如果你删除这个goto :eof命令,控制将继续到 :AnotherLabel 的代码,然后返回主线逻辑。但是对于这个命令,下面的代码 :AnotherLabel 永远不会执行。

由于 goto :eof 命令有两种不同的用法,所以我通常在这些命令后面加上一个注释,定义它要终止什么,或者是例程的名称,或者是bat文件本身。我只是将rem命令放在一个&号后面,它在一行代码中将两个命令分隔开。从编程的角度来说,这是不必要的,但是这种做法极大地增强了代码的可读性,特别是当例程比前面的示例更长、更复杂时。

调用Bat文件

短或重复的代码位是内部例程的最佳候选;您可以在bat文件的末尾添加一个或多个例程,以创建一个组织良好的模块,您可以为此感到自豪。但有时这些简短的代码并不那么短,或者它们非常有用,以至于您希望将它们提供给您编写的其他bat文件,甚至可能是其他人。这个场景没有使用例程,而是调用一个bat文件调用另一个bat文件。例如,您可以创建一个bat文件来处理日志记录,并从多个其他bat文件调用它。

从一个bat文件执行另一个bat文件的工作方式与执行内部例程略有不同。但首先,让我们回到前面的编译程序是如何执行的。当解释器遇到一行只是可执行文件名称的代码时,它调用可执行文件。因此,这个“命令”执行程序:D:\Batch\10\MyProg.exe

程序完成其任务后,控制返回到bat文件。您可能期望对bat文件的调用以同样的方式工作,但是遗憾的是,事实并非如此。然而,下面的代码行确实执行了被调用的bat文件,但是使用了一个巨大的bat:call D:\Batch\10\CalledBat.bat

总而言之,无论是调用bat文件还是调用另一种语言的编译可执行文件,您都可以使用call命令或省略它,但这是有区别的。在调用可执行文件时,这两种技术实际上是相同的。在调用同类bat文件时,调用命令确保将控制权返回给调用者。如果没有命令,控制就永远不会返回。

因为我从来没有发现不返回的bat文件调用有什么用途,所以我总是倾向于忽略可执行文件的调用命令,而将其用于bat文件。一个优点是,一眼就能看出调用的是什么类型的文件。

在我职业生涯的早期,当我无法弄清楚为什么我的bat文件停止执行时,我了解到调用命令关于 bat文件的必要性。没有挂起或中止消息;它就这么停了。更复杂的是,我的故障排除可以集中在所谓的bat文件上。过了好一会儿,我才注意到那个丢失的 call 命令,更重要的是,我明白了它的重要性。但这并不是call命令的唯一特性。

调用标签注意事项

在前文中,我提到可以在goto命令的参数中将冒号从标签名称中去掉,尽管强烈建议包括它。使用call命令,在调用定义内部例程的标签时总是需要冒号。

这种明显的不一致可能没有意义,除非您考虑到goto命令只涉及到其bat文件中的标签,而call命令调用其bat文件内部和外部的实体。结果是,当尝试调用 :MyLabel 而不带冒号时,会发生一些非常意想不到的事情:call MyLabel

冒号会告诉解释器调用内部例程,但解释器却试图调用外部文件。首先,它在当前目录中查找可执行文件,如MyLabel.com或MyLabel.exe。然后,它在当前目录中查找MyLabel.bat和其他一些具有此文件名的可执行文件类型。然后,它遍历path变量中的所有目录,拼命寻找任何名为MyLabel的可以执行的内容。如果没有找到这样的文件,解释器将不会查找该名称的标签,即使 :MyLabel 是bat文件中的有效标签;相反,它会生成一个错误。

当使用goto或call命令导航到标签时,为了保持一致性,请务必使用冒号。

重要:

当没有找到标签时,goto命令会中止进程。call命令更容易理解一些。当它的参数是一个无效的标签时,它们都写出一条错误消息,但是调用命令也将errorlevel设置为1。如果您选择不询问返回代码,则该过程将若无其事地继续进行,就好像什么都没有发生一样。

启动Bat文件

有时,您可能希望启动或生成一个bat文件作为一个新进程。也就是说,您可能希望启动另一个bat文件,但不希望解释器在继续之前等待它完成。例如,您可以并行执行多个进程以加快总体处理时间。您可以剥离出一个非关键但耗时的任务,比如一个日志记录进程,让它在自己的时间内执行。在后文中,我将讨论如何自动 kill 和重新启动挂起的进程。为了实现这一点,我将把容易挂起的进程作为一个独立的bat文件生成,并从主bat文件监视它。

要启动或生成一个bat文件,只需使用start命令代替call命令:start D:\Batch\10\LaunchedBat.bat

该命令创建第二个命令或DOS窗口,其中文件 LaunchedBat.bat 与启动它的bat文件同时执行。

exit 命令

您可能会想到,exit 命令退出例程、bat文件或整个执行,它甚至可以设置返回代码。它在功能上与 goto :eof 命令有重叠,但我很快就会展示一个重要的区别。

不带参数的exit命令会突然结束整个进程。遗憾的是,第二个echo命令不会被执行:

> con echo The meaning is Life is...
exit
> con echo ... %meaningOfLife%

第一个echo命令将其消息写入控制台,但是exit命令在您可以读取它之前关闭了窗口。无论在哪里调用退出命令,都会发生这种情况——在高级bat文件中,在被调用的bat文件中,甚至在任一类型bat文件中的例程中。

然而,文档不清楚B代表什么,但对我来说,它代表break,因为下面的命令从被调用的代码中跳出,无论是被调用的bat文件还是bat文件中的例程:exit /B

该命令只有在高级bat文件的主逻辑中调用时才退出整个进程。它不会改变errorlevel,逻辑上等同于 goto :eof。这两个命令都是有效的,其用法通常取决于个人偏好。我使用的是 goto :eof命令,但只在不需要返回代码的情况下使用。

在前面的文章中,我们介绍过基本中止逻辑,但将其解释留到后面,也就是现在。

:Abort
  echo The Process is aborting
  exit /B 1

这个退出命令的行为与 exit /B 类似,但有一个例外。当控制返回到调用代码的位置时,该选项后面的命令的数字参数变成errorlevel中包含的新值。简而言之,该命令脱离bat文件或例程并返回退出或返回代码。在前面的例子中,返回码是1。但是,如果没有检测到错误,则bat文件的主逻辑可能以将返回代码设置为0结束:exit /B 0

如果检测到致命错误,主线逻辑中的 goto :Abort 命令将把解释器引导到中止逻辑。必须使用 goto 命令,因为 call 命令会将中止逻辑视为被调用的例程;将设置错误级别,但控制权将返回到致命错误的位置。但是当使用goto命令导航到标签时,不会调用例程;它仍然被认为是在主线逻辑中,并且exit命令结束了bat文件,而不是一个例程。

为了更灵活,你可以为退出代码创建一个变量,针对不同的失败将其设置为不同的值:

:Abort
 echo The Process is aborting
 exit /B %exitCode%

然后,可以通过bat文件中的多个goto命令访问该逻辑。

(实际的中止例程将比这个简单的echo命令有趣得多。错误消息可以是多行,并且具有可变的内容,所有内容都写入日志文件和控制台,但我在这里对其进行了简化,以便将重点放在退出命令上。)

总结

在本文中,我详细介绍了调用内部例程和其他bat文件的不同方法。您已经学习了如何从这些调用中返回,或者如何简单地从任何地方突然结束整个过程。您还学习了如何启动或生成另一个bat文件,该文件完全独立于第一个bat文件。最重要的是,您现在了解了goto和call命令之间的重要而微妙的区别。简而言之,调用返回控制并且可以到达它的bat文件之外,而goto则两者都不做。

这个谜题还有一大块没解开。调用的bat文件可以向被调用的bat文件传递多个参数,而被调用的bat文件甚至可以设置和传递参数作为返回。这比人们想象的要复杂得多,我将在后面文章中详细说明所有的细微差别。

本文由博客一文多发平台 OpenWrite 发布!

标签:10,bat,调用,goto,文件,例程,命令
From: https://www.cnblogs.com/chuanqi1415583094/p/18411790

相关文章

  • VU9P加速卡设计原理图 :410-基于XCVU9P+ C6678的100G光纤的加速卡
    基于XCVU9P+C6678的100G光纤的加速卡一、板卡概述     二、技术指标 •  板卡为自定义结构,板卡大小332mmx260mm; •  FPGA采用Xilinx Virtex UltralSCALE+ 系列芯片 XCVU9P; •  FPGA挂载4组FMC HPC 连接器; •  板载4路QS......
  • 郑轻刷题知识1031-1040
    分类比较少的话用if分类较多的话,用case(是符号的话不要忘了加引号,例如' +')1036:(a为年份,b为月份)  switch(b)  {case1:case3:case5:case7:case8:case10:case12:   printf("31");//1.3.5.7.8.10.12是31天  break;  case2:   ......
  • error:0308010C:digital envelope routines::unsupported
    参考——https://www.jb51.net/javascript/315177xde.htm报错信息————10%building2/2modules0active(node:5732)[DEP0111]DeprecationWarning:Accesstoprocess.binding('http_parser')isdeprecated.(Use`node--trace-deprecation...`toshow......
  • 京东h5st4.7.4(9段) 价值1000元?纯算奶妈级教学
    网站:aHR0cHM6Ly93d3cuamQuY29tLw==接口:aHR0cHM6Ly9hcGkubS5qZC5jb20=0.闲聊京东的h5st看着吓人一打开f12就显示本页面由京东-主站前端团队开发维护           --JDC其实过程很明显,经过了6,7个平坦流才可以拿到结果主要细心一点,相信你一定也......
  • mybatis exists 中使用代替in关键字
      使用场景,in适合数据量小的时候,exists适合数据量大的时候。<iftest="torqueRecordPageDTO.vinList!=nullandtorqueRecordPageDTO.vinList.size>0">andexists(select1from(<foreachcollection="......
  • 【2024潇湘夜雨】WIN10_LTSC2021_21H2.19044.4894软件选装纯净特别版9.12
    【系统简介】=============================================================1.本次更新母盘来自WIN10_LTSC2021_21H2.19044.4894.2.全程离线精简、无人值守调用优化处理制作。部分优化适配系统可能要重启几次,即使显示适配失败也不要在意,可能部分优化不适用。3.OS版本号为19044.48......
  • 洛谷P10504 守卫者的挑战 题解 概率DP
    题目链接:https://www.luogu.com.cn/problem/P10504状态\(f_{i,s,k}\)表示:当前正面临第\(i\)项挑战(此时第\(1\simi-1\)项挑战已完成,第\(i\)项挑战还没开始);目前已经挑战成功了\(s\)项(即第\(1\simi-1\)项挑战中共有\(s\)项挑战成功,\((i-1)-s\)项没挑战成功);......
  • 0910-0911 shell编程与基础算法(leetCode )
    栈的定义栈(Stack),也称为堆栈,它是一种特殊的线性表,只允许在表的一端进行插入和删除操作。允许在表操作的一端称为栈顶(Top),另一端称为栈底(Bottom)。栈顶是动态变化的,它由一个称为栈顶指针(top)的变量指示。当表中没有元素时,称为空栈。栈的插入操作称为入栈或进栈,删除操作称为出栈或......
  • mybatis in中超过1000个值解决办法(超简单)
    众所周知sql中条件in的值是不能超过1000个的,而mybatis可以使用动态sql拼接的方式绕开这个限制,网上看了很多例子,我感觉都不太好理解,下面介绍一个超简单的例子。select*fromuser_infowhere1=1<iftest="userList!=nullanduserList.size()>0">and(userIdin<f......
  • 代码随想录算法训练营,9月12日 | 513.找树左下角的值,112. 路径总和,106.从中序与后序遍
    513.找树左下角的值题目链接:513.找树左下角的值文档讲解︰代码随想录(programmercarl.com)视频讲解︰找树左下角的值日期:2024-09-12想法:1.迭代:用层序遍历,遍历每层时记录下第一个节点的值,到最后一层就是要求的值;2.递归:根据最大的深度来找目标值。Java代码如下://迭代classSolut......