bat脚本外部调用cmd
介绍。许多人问我是否可以从Access中控制外部过程。 通过外部流程,我说的是在Access外部运行脚本。 这将包括BAT,CMD和PowerShell脚本,甚至包括使用Windows的FTP.EXE命令行界面的FTP脚本。
这些脚本接口的功能很难加以限制。 CMD.EXE尤其可以控制几乎所有命令的整个脚本文件。 可以在Access中将其自动化。
这种脚本的我最喜欢的用途之一是升级项目的当前版本。 我会在启动时自动更新前端数据库,并且还提供了以后随时进行升级以防损坏的选项(不幸的是,某些Access数据库很容易出现)。
这就是我将在本文中解释的内容。 在同一主题领域中的另一个是
使用Scripts-FTP控制外部进程 。 总体概念。这个概念依赖于一个表,最简单的设计为:
表格= [ tblCMD ] Field Name Type PK (Compound)
Template Boolean #1
Type String(1) #2
LineNo Long #3
Cmd String(255)
在某些情况下,我在此数据库升级CMD脚本期间遇到了问题,单行中的命令长度有可能超过允许的255个字符。
因此,在这种情况下,在我所附的示例中,我使用两个字段[Cmd1]和[Cmd2]代替了单个[Cmd]。
在处理要输入到脚本表中的数据时,要记住的一个非常重要的观点是,数据中具有可替换参数的能力带来了额外的优势。 在我的示例中,我使用百分比(%)后跟两个字母字符来标记数据中要从代码中插入值的点。 升级CMD脚本的一个示例是%Ac,它用于Access可执行文件的全名。 FTP脚本的用法不同,因此请注意不要混淆两者。 这是升级CMD脚本替换以及FTP脚本使用的值的列表。 替换参数与其相关变量具有相同的名称,除了%替换str。 所以,
代码中的strBa用于替换数据中%Ba的出现。下一步是为同一表中的特定脚本创建模板的更新副本。 由于[Template]值为False,因此可以识别此新数据。 更新内容是用所需的值替换前面提到的参数。 然后(通过查询)将此新数据(仅)导出到脚本文件。导出后,该数据将从表中删除。
完成所有操作并准备好脚本文件后,我们需要调用它。
示例数据。 表格= [ tblCMD ] Template Type Order Cmd1 Template Type Order Cmd1
TRUE U 10 @ECHO OFF FALSE U 10 @ECHO OFF
TRUE U 20 IF NOT EXIST "%Fo\%Ba" GOTO RENAMEBACKUP
FALSE U 20 IF NOT EXIST "D:\Scratch\Access\ControlExternalProcesses\ContEx_Last.Accdb" GOTO RENAMEBACKUP
TRUE U 30 ATTRIB -R "%Fo\%Ba" >NUL FALSE U 30 ATTRIB -R "D:\Scratch\Access\ControlExternalProcesses\ContEx_Last.Accdb" >NUL
TRUE U 40 DEL "%Fo\%Ba" >NUL FALSE U 40 DEL "D:\Scratch\Access\ControlExternalProcesses\ContEx_Last.Accdb" >NUL
TRUE U 50 IF ERRORLEVEL 1 GOTO BADBACKUP FALSE U 50 IF ERRORLEVEL 1 GOTO BADBACKUP
TRUE U 60 :RENAMEBACKUP FALSE U 60 :RENAMEBACKUP
TRUE U 70 ECHO Attempting to rename '%Fo\%Or' to '%Ba'
FALSE U 70 ECHO Attempting to rename 'D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb' to 'ContEx_Last.Accdb'
TRUE U 80 ECHO Needs "%Fo\%Or" to be closed before continuing...
FALSE U 80 ECHO Needs "D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb" to be closed before continuing...
TRUE U 90 :START FALSE U 90 :START
TRUE U 100 REN "%Fo\%Or" "%Ba" >NUL FALSE U 100 REN "D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb" "ContEx_Last.Accdb" >NUL
TRUE U 110 IF ERRORLEVEL 1 GOTO START FALSE U 110 IF ERRORLEVEL 1 GOTO START
TRUE U 120 ATTRIB +R "%Fo\%Ba" >NUL FALSE U 120 ATTRIB +R "D:\Scratch\Access\ControlExternalProcesses\ContEx_Last.Accdb" >NUL
TRUE U 130 ECHO Attempting to copy '%Ne' to '%Fo\%Or'
FALSE U 130 ECHO Attempting to copy 'D:\Scratch\Access\ControlExternalProcesses\Release\ContEx01.00.00.Accdb' to 'D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb'
TRUE U 140 COPY /B /V /Y "%Ne" "%Fo\%Or" >NUL FALSE U 140 COPY /B /V /Y "D:\Scratch\Access\ControlExternalProcesses\Release\ContEx01.00.00.Accdb" "D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb" >NUL
TRUE U 150 IF ERRORLEVEL 1 GOTO BADCOPY FALSE U 150 IF ERRORLEVEL 1 GOTO BADCOPY
TRUE U 160 ATTRIB -R -S -H "%Fo\%Or" >NUL FALSE U 160 ATTRIB -R -S -H "D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb" >NUL
TRUE U 170 IF ERRORLEVEL 1 GOTO BADCOPY FALSE U 170 IF ERRORLEVEL 1 GOTO BADCOPY
TRUE U 180 ECHO Restarting Access with the updated file.
FALSE U 180 ECHO Restarting Access with the updated file.
TRUE U 190 START "Dummy Title" /D"%Fo" "%Ac"
[Cmd2] "%Fo\%Or" ;%BE
FALSE U 190 START "Dummy Title" /D"D:\Scratch\Access\ControlExternalProcesses" "C:\Program Files (x86)\Microsoft Office\Office14\MSAccess.Exe"
[Cmd2] "D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb" ;
TRUE U 200 GOTO CMDEND FALSE U 200 GOTO CMDEND
TRUE U 210 :BADBACKUP FALSE U 210 :BADBACKUP
TRUE U 220 ECHO Unable to delete existing backup file '%Fo\%Ba'.
FALSE U 220 ECHO Unable to delete existing backup file 'D:\Scratch\Access\ControlExternalProcesses\ContEx_Last.Accdb'.
TRUE U 230 GOTO CMDERROR FALSE U 230 GOTO CMDERROR
TRUE U 240 :BADCOPY FALSE U 240 :BADCOPY
TRUE U 250 ECHO Unable to copy '%Ne' to '%Fo\%Or' successfully.
FALSE U 250 ECHO Unable to copy 'D:\Scratch\Access\ControlExternalProcesses\Release\ContEx01.00.00.Accdb' to 'D:\Scratch\Access\ControlExternalProcesses\ContEx.Accdb' successfully.
TRUE U 260 :CMDERROR FALSE U 260 :CMDERROR
TRUE U 270 ECHO Please refer this problem to Support.
FALSE U 270 ECHO Please refer this problem to Support.
TRUE U 280 PAUSE FALSE U 280 PAUSE
TRUE U 290 :CMDEND FALSE U 290 :CMDEND
TRUE U 300 DEL "%Fo\UPGRADE.CMD" >NUL FALSE U 300 DEL "D:\Scratch\Access\ControlExternalProcesses\UPGRADE.CMD" >NUL
注意
左边的数据是原始模板数据。
右边的数据是更新后的数据,这就是为创建实际脚本文件而导出的数据。
数据说明。
这是一个CMD脚本,在Access数据库随后关闭之前,Access代码将很快(但延迟很小)调用它。 总体目的是用新版本替换Access数据库文件,然后像旧文件一样调用新文件。 为了安全起见,当前文件将以相同的名称保存,但要附加“ _Last”。 如果有任何问题,只需在新文件上重命名即可重新启用该文件。
许多生产线对于整个过程而言都不那么重要,而仅仅是为了提供尽可能可靠的过程。 我将我的解释限于基本原则。
- 第20行到第50行确保在重命名现有的_Last文件之前,将其删除。
- 从#60行到#110行尝试将当前文件重命名为_Last文件。
仅当当前文件成功关闭后,此功能才能起作用。 这个过程会不断尝试,直到成功为止。
此概念的另一个变体是简单地在循环中将文件重命名为其自身。 一旦成功,您就知道该文件是免费的。 - 从#120行到#170行复制文件的新版本来代替当前版本。
- 从#180行到#190行再次将Access重新启动,直接进入当前数据库。
它使用[Cmd2],因为它还将参数传递给数据库,告诉数据库要链接到的后端文件。 通常不需要这样做,但这说明了如何做到这一点。 - 从#210行到#280行处理错误情况。
- #290和#300行通过删除脚本文件并结束来结束该过程。
尽管我展示的某些代码行将引用我自己的一些例程,但这只是代码的一小部分,即使未显示代码,也应很清楚它的作用。 因此,我将展示和解释主要代码,并将整个数据库包括在附件中,以便在需要时可以探索其他任何例程。 如果需要,我的代码始终可以重复使用。 我唯一声称的是版权。 其他人可以自由使用和更改代码。
我先解释一下
MultiReplace()
是我的功能之一,它通过允许多对from和to替换而简单地扩展了VBA.Replace()
函数。
不过,我不在SQL中使用此功能,正如您从第26行至第37行中的Replace()
函数调用的笨拙用法中看到的那样。
这是替换参数的地方。
要查看其效果,请在第60行上放置一个断点,并在代码中的该位置打印strSQL的值。
我已经包含了
下面的SetStrings()
。
它基本上用于获取我们正在运行项目的文件夹以及其他所需的值。
- 到#19行的位置进行设置并清除所有文件。
- 第20行确定是否存在可用于替换当前文件的升级文件。
此逻辑与该概念无关。 这只是使用它的一种方式的示例。 - 第22行使用找到的文件中的版本号设置变量strVersion。
- 第23行查找当前文件名的最后一个点(。)。 IE浏览器 扩展名在哪里。
- 第24行到#25行设置了strBa,它是数据(%Ba)中使用的可替换参数之一。
- 第26行到第37行设置了strCmd的值,该值用于将所有可替换参数替换为SQL中的值。
- 第38行至第59行使用strCmd两次设置了SQL。 一次用于[Cmd1],另一次用于[Cmd2]。
- 第60行设置dbVar指向当前数据库。
- 第62行保存“ 自动压缩”属性的值。 如果过程失败并需要恢复,这一点很重要。
- 第62行至第63行使用strSQL中SQL将实时数据添加到表中,但是仅在清除了之前可能遗留的所有数据之后。
此数据将用于创建CMD文件。 - #64和#75行设置了intErr,以向ErrorHandler指示在进程崩溃时需要执行哪些操作来恢复。
- 第65行到第71行首先删除任何文件(如果存在),然后使用
DoCmd.TransferText
将新数据导出到该DoCmd.TransferText
。
注意 该文件必须是.TXT文件,此文件才能起作用。 - 第72行至第73行首先清除实际CMD文件(如果存在),然后将.TXT文件重命名为.CMD。
- 第74行现在已导出并且不再需要删除表中的新数据。
- 第76行将“ 自动压缩”属性设置为off,以避免任何延迟关闭当前数据库。
- 从#76行到#83行提示操作员,并警告他们即将发生的事情。
- 第84行调用了我们刚刚创建的CMD文件。
这将一直循环运行直到成功关闭数据库为止,然后它将复制和重命名文件,然后最终在Access中调用新数据库并删除自身(CMD文件)。
'UpgradeProject() prepares to upgrade from strN and returns true if all ok.
' If all ok then calling code needs to close and quit the whole application
' in order for the process to continue.
'12/09/2012 strBE used to ensure the upgraded FE uses the current BE or one
' passed in the shortcut.
Public Function UpgradeProject() As Boolean
Dim intX As Integer, intErr As Integer, intCompact As Integer
Dim strNe As String, strVersion As String, strSQL As String
Dim strMsg As String, strMode As String, strCmd As String
Dim dbVar As DAO.Database
On Error GoTo Error_UpgradeProject
strMode = SwitchMode(strType:="Process")
Call SetStrings
'Before we go any further, and regardless of whether or not an upgrade is
' even required, let's clear away any existing copy of UPGRADE.CMD.
' It's checked again immediately prior to being created.
If Exist(strFo & "\UPGRADECMD.Txt") Then _
Call KillFile(strFo & "\UPGRADECMD.Txt")
strNe = GetUpgradeFile()
If strNe = "" Then Exit Function
strVersion = FormatVersion(strNe, "Display")
intX = InStrRev(StringCheck:=strOr, StringMatch:=".")
strBa = MultiReplace("%S_Last%F", "%S", Left(strOr, intX - 1), _
"%F", Mid(strOr, intX))
strCmd = "Replace(Nz([~C],''),'%Ac','%sAc')"
strCmd = Replace("Replace(%C,'%Ba','%sBa')", "%C", strCmd)
strCmd = Replace("Replace(%C,'%BE','%sBE')", "%C", strCmd)
strCmd = Replace("Replace(%C,'%Fo','%sFo')", "%C", strCmd)
strCmd = Replace("Replace(%C,'%Ne','%sNe')", "%C", strCmd)
strCmd = Replace("Replace(%C,'%Or','%sOr')", "%C", strCmd)
strCmd = MultiReplace(strCmd, "%sAc", strAc, _
"%sBa", strBa, _
"%sBE", "", _
"%sFo", strFo, _
"%sNe", strNe, _
"%sOr", strOr)
strSQL = "INSERT INTO [tblCMD]%L" _
& " ([Template]%L" _
& " , [Type]%L" _
& " , [Order]%L" _
& " , [Cmd1]%L" _
& " , [Cmd2])%L" _
& "SELECT [Template]%L" _
& " , [Type]%L" _
& " , [Order]%L" _
& " , [C1] AS [Cmd1]%L" _
& " , IIf([C2]='',Null,[C2]) AS [Cmd2]%L" _
& "FROM (SELECT False AS [Template]%L" _
& " , [Type]%L" _
& " , [Order]%L" _
& " , %C1 AS [C1]%L" _
& " , %C2 AS [C2]%L" _
& " FROM [tblCMD]%L" _
& " WHERE ([Template])%L" _
& " AND ([Type]='U')) AS [qC]"
strSQL = MultiReplace(strSQL, "%C1", Replace(strCmd, "~C", "Cmd1") _
, "%C2", Replace(strCmd, "~C", "Cmd2") _
, "%L", vbNewLine)
Set dbVar = CurrentDb()
intCompact = dbVar.Properties("Auto Compact")
Call ClearTable(strTable:="tblCMD", strWhere:="(NOT [Template])")
Call dbVar.Execute(Query:=strSQL, Options:=dbFailOnError)
intErr = &H1
If Exist(strFo & "\UPGRADECMD.Txt") Then _
Call KillFile(strFo & "\UPGRADECMD.Txt")
Call DoCmd.TransferText(TransferType:=acExportDelim, _
SpecificationName:="Upgrade Spec", _
TableName:="qryUpgrade", _
FileName:=strFo & "\UPGRADECMD.Txt", _
HasFieldNames:=False)
If Exist(strFo & "\UPGRADE.CMD") Then Call KillFile(strFo & "\UPGRADE.CMD")
Name strFo & "\UPGRADECMD.Txt" As strFo & "\UPGRADE.CMD"
Call ClearTable(strTable:="tblCMD", strWhere:="(NOT [Template])")
intErr = &H0
dbVar.Properties("Auto Compact") = 0
strMsg = MultiReplace("Upgrading from '%N'.%L%L" _
& "This process should be very quick (<1 minute).%L" _
, "%N", strNe _
, "%L", vbNewLine)
Call MsgBox(Prompt:=strMsg, _
Buttons:=vbInformation Or vbOKOnly, _
TITLE:=CurrentProject.NAME)
Call Shell(PathName:=strFo & "\UPGRADE.CMD", WindowStyle:=vbNormalFocus)
Call SwitchMode(strType:=strMode)
UpgradeProject = True
Exit Function
Error_UpgradeProject:
If Not dbVar Is Nothing Then dbVar.Properties("Auto Compact") = intCompact
If Exist(strFo & "\UPGRADE.CMD") Then Call KillFile(strFo & "\UPGRADE.CMD")
If Exist(strFo & "\UPGRADECMD.Txt") Then _
Call KillFile(strFo & "\UPGRADECMD.Txt")
If (intErr And &H1) Then _
Call ClearTable(strTable:="tblCMD", strWhere:="(NOT [Template])")
strMsg = MultiReplace("Error (%N) :%L%D%L%L" & _
"Unable to complete upgrade process", _
"%N", Err, _
"%D", Err.DESCRIPTION, _
"%L", vbNewLine)
Call MsgBox(Prompt:=strMsg, Buttons:=vbCritical Or vbOKOnly, TITLE:=strOr)
Call SwitchMode(strType:=strMode)
End Function
'SetStrings() prepares the global string variables strA, strB, strF & strO.
Public Sub SetStrings()
If strAc = "" Then
With CurrentProject
strAc = BareFolder(SysCmd(acSysCmdAccessDir)) & "\MSAccess.Exe"
strOr = .NAME
strFo = Left(.Path, 1)
If strFo >= "A" And strFo <= "Z" And strFo <> Left(CurDir, 1) Then _
Call ChDrive(Drive:=strFo)
strFo = BareFolder(.Path)
If BareFolder(CurDir) <> strFo Then Call ChDir(Path:=strFo)
End With
End If
End Sub
结论。
在Access中可以控制的内容几乎没有限制。 我包括一个
附件 ,可以提取到任何文件夹并运行。 附件使用外接程序工具栏来访问要测试的项目。 第一次运行时,它可能会立即升级您。 不用担心 升级版本完全相同,只是内部存储了更高的版本号。 使用加载项工具栏手动测试升级过程。翻译自: https://bytes.com/topic/access/insights/966089-control-external-processes-using-scripts-cmd
bat脚本外部调用cmd