如何使用批处理脚本创建其他脚本?

5
我正在创建一个脚本,希望它具有双重功能:
1. 脚本将使用用户输入的选项执行命令以更改某些设置。这部分已经完成。 - 脚本通过SETLOCAL/ENDLOCAL和SET /P将用户的选择收集到本地环境变量中,并在脚本后面调用这些变量以执行命令。 - 在收集用户选项之后,有一个验证步骤显示这些选项并要求用户确认后再进行命令执行。
2. 脚本将为用户提供一个选项,自动创建一个包含他们选项的新脚本以便将来使用。
以下是我目前实现目标#2的计划概念,尽管我不确定如何编写它:
1. 构建一个模板脚本,可以复制以启动定制脚本的创建。 - 在模板脚本的开头处,SETLOCAL将隔离由最终脚本创建的环境变量,以便在脚本完成运行后不会保留它们。 - 紧接着SETLOCAL之后将是一个GOTO命令,该命令指向脚本末尾的标签。该标签将是已完成脚本中变量定义部分的开始。 - 在GOTO命令之后将是一个名为STARTEXEC的标签,该标签将在变量定义完成后跳转。 - 在变量部分标签之前将是一个带有一些命令的已标记部分,用于退出脚本。
2. 在当前脚本中: - 在验证步骤期间添加一个CHOICE,询问用户是否要创建静态脚本。如果是,请通过SET /P提示用户输入文件名,然后通过GOTO在此脚本的执行流程中包含脚本构建命令。
3. 使用类似以下命令的命令来构建静态脚本的变量部分:
COPY ScriptTemplate.bat %UserFileName%.bat ECHO SET VAR1=%UserVar1% >> %UserFileName%.bat ECHO SET VAR2=%UserVar2% >> %UserFileName%.bat
4. 添加一行以确保静态脚本跳转到命令执行。
ECHO GOTO STARTEXEC >> %UserFileName%.bat
我的问题是:
  1. 我的方法是否相当可靠,还是有更好的方法可以通过批处理脚本完成?

  2. 在静态脚本回到变量部分之前,我应该使用什么来退出静态脚本?我知道我需要一个ENDLOCAL。这里EXIT可行吗,还是有其他更合适的方法?


你是否受限于批处理文件? - Anthony Miller
@Mechaflash 只能根据我的目前的知识水平和经验回答,但是是的。这个问题的范围仅限于批处理文件。 - Iszi
我已经给出了答案,但是我建议你将来在这样的项目中考虑使用AutoIT。它非常容易上手,而且你似乎已经很熟练了。 - Anthony Miller
@Mechaflash 很好的回答,但是当涉及到循环时,我还是有点迷惑。 - Iszi
2个回答

1

模板的想法似乎是最简单的方法(不确定是否短,但这可能是我会采用的方法)。基于原始模板创建一个单独的模板,删除所有 SET /P 命令。

编辑:

已删除旧答案。

在模板末尾写入:

GOTO:EOF

:User_Defined_Variables

在文件开头写上:


CALL:User_Defined_Variables

在您的原始脚本中,用户确认创建自定义脚本后,请复制模板文件COPY /y template.bat userscript.bat并将所有用户变量发送到其中。
(ECHO var1 = %uservar1% 
 ECHO var2 = %uservar2%
 Echo var3 = %uservar4%)>> userscript.bat
GOTO:EOF

因此,当用户运行脚本时,CALL:User_Defined_Variables将设置用户的脚本参数,并继续执行其余部分的脚本。


好主意,但我对循环如何工作感到困惑。(循环从来不是我的强项。)特别是,它似乎只能在指定数量的行中起作用。如果行数是可变的呢? (某些用户配置将需要定义所有变量,某些则不需要。)这就是为什么我认为仅使用>>将变量放在末尾,并使用GOTO根据需要跳转到结果脚本周围可能更容易的原因。 - Iszi
给我一段代码片段(只需更新您的原始帖子),其中您要求用户输入,并基于输入运行的命令,我可以组合一个真实的示例。 - Anthony Miller
完全改变了我的答案。 - Anthony Miller

0

Iszi,

我在工作中也有类似的功能...我们有一组不同的自定义变量,用于启动我们处理的每个客户的其他函数。我编写了一个使用批处理的复杂脚本,使我和同事能够实现这一点,但是我认为每次创建单独的批处理文件都是不必要的。

我认为有两种方法可以实现你想要的:

  1. 您可以在开始时设置所有变量并从那里进行编辑

  2. 您可以每次提示输入变量/编写新脚本

无论哪种方式,编写一个批处理文件来编写另一个批处理文件是愚蠢的,并且会在代码中留下各种潜在错误。您不仅需要对一个批处理脚本进行错误检查,还需要对另一个脚本进行检查。

因此,以下是一些示例:

1.

@ECHO off
TITLE Install Script
COLOR 0A
SETLOCAL

REM ---------------------------
REM  *** EDIT VARIABLES BELOW ***
REM ---------------------------

:sharepath
set sharepath=\\SCIncapps\sharepath
REM SHAREPATH = The location of the files that will be copied to the client machine. List of files you will need in your SCIncIntViewer folder: 
REM vcredist_x86.exe, msxml6.msi,  Browser Access Client Controls.msi,  Automated Update Service.msi, bac.ico, dotnetfx35.exe, dotNetFx40_Full_x86_x64.exe,  Episys Client.msi
REM This Sharepath is where the Script needs to be located and has to be an already existing directory

:primaryStaging
set primaryStaging=SCIncweb
REM primaryStaging = The name of the back office server that handles AutoUpdate. This is usually the same as websrvr, except in distributed  environments

:webserver
set websrvr=SCIncweb
REM websrvr = The name of the Web Server; i.e. SCIncweb, SCInc14Doc1, 10.1.1.1, 192.168.1.20, etc.

:install_variables
set dotnet35=F
REM Defines the installation of .NET 3.5 SP 1 as True(T) or False(F)
set dotnet40=F
REM Defines the installation of .NET 4.0 as True(T) or False(F)
set regedit=F
REM Defines the creation of the Registry File to edit all IE settings as True(T) or False(F)
set SSL=F
REM Defines the use of SSL for the BAC URL; SSL is set as True(T) or False(F)
set org=0000
REM Defines the Organization for the BAC url shortcut that is created on the desktop for all users (enter the numeric org #)
set favorite=T
REM This will put the  Browser Access Client URL in their favorite folder and on their browser's quick link bar as True(T) or False(F)
set reboot=F
REM Defines whether or not the script will reboot the machine after it's complete as True(T) or False(F)
REM If using the PsExec.exe to push this script this will force the Domain Administrator to logoff the machine
set reloadAll=F
REM This defines if the _Installed_Files.txt will be overwritten to install all products again as True(T) or False(F)
REM NOTE This will copy down all items and install everything except .NET 3.5 SP1 and .NET 4.0

:creditUnion_Variables
set episys=F
REM Defines the installation of Episys Integration for  as True(T) or False(F)

:bank_Variables
set silver2020=F
REM Defines the installation of SCInc Integration for  as True(T) or False(F)
set coreDirector=F
REM Defines the use of Core Director for banks as True(T) or False(F)
set iseries=1.1.1.1
REM Defines the IP address of the iSeries if in-house Some Company, Inc Core, otherwise irrelevant
set dualinQuiry=F
REM Defines the deletion of the old SCInc Viewer if NOT using Dual Inquiry as True(T) or False(F)
set thinClient=F
REM Defines the /install and /execute flags on a Terminal Server as True(T) or False(F)
set vportsPath=%sharepath%
REM Defines the vports.xml file's location, this variable should be kept as the sharepath unless the client specifies otherwise

:Logs
set PCaudit=T
REM Defines whether or not you want just the PC name to be logged to a file as True(T) or False(F)
REM Creates a list of all the PC's that this script has been run on and puts it in the sharepath folder

REM ---------------------------
REM *** DO NOT EDIT BELOW ***
REM ---------------------------

IF /I '%scanStation%'=='T' goto:eof
REM If running the Scan Station Install tool only the variables are read and then the  Scan Station Install Tool.bat will resume
goto thinClient_inst

:thinClient_inst
IF /I '%thinClient%'=='F' goto date_time
change user /install

:date_time
FOR /F %%A IN ('TIME/T') DO SET time=%%A
REM Sets the time in a proper format for Error Logging
FOR /F "tokens=1-4 delims=/ " %%B IN ('DATE /t') DO SET date=%%C/%%D/%%E
REM Sets the date in a proper format for Error Logging

:errorLog
REM Logs the error messages for each product that the script attempts to install
IF exist %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt goto PCaudit
echo.Microsoft Website for Errors:> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt
echo.http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt
echo.(Popular Error: 9009, 1616 :: File does not exist in directory)>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt
echo.--------------------------------------------------------------->> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt
set cleanInstall=0
REM Checks if the ErrorLog file already exists, if not it writes the fist 2 lines which contain the URL for determining the Errors from Microsoft
goto PCaudit

:PCaudit
REM Logs the Computer Name to the Installed_PC text file
REM This checks to make sure that the PC name doesn't already exist in that file and if it does it puts it into another file called %org%_ _Repeat_Installs_Log.txt
verify >nul
IF /I '%PCaudit%'=='f' set PCaudit=F
IF /I '%PCaudit%'=='F' goto start
IF /I '%PCaudit%'=='t' set PCaudit=T
set repeat=F
findstr /x "%computername%" %sharepath%\%org%_ _Installed_PCs_AuditList.txt 
IF '%ERRORLEVEL%'=='0' set repeat=T
IF /I '%repeat%'=='T' echo.%date% - %time% - %computername%>> %sharepath%\%org%_ _Repeat_Installs_Log.txt
IF /I '%repeat%'=='T' goto start
IF /I '%repeat%'=='F' echo.%computername%>> %sharepath%\%org%_ _Installed_PCs_AuditList.txt
goto start

:start
REM Changes all variables for True(T) and False(F) to capital letters since batch is case sensitive
set HKEY=HKEY_LOCAL_MACHINE
REM This script is meant to be run by a user with ADMIN privledges; 
REM " HKEY_LOCAL_MACHINE " registry items can only be edited by an admin

:_done
IF NOT exist "c:\SCInc\ \_done" md c:\SCInc\ \_done
IF NOT exist "c:\SCInc\ \_done\ _Installed_Files.txt" echo. Installed Files for %computername%: > c:\SCInc\ \_done\ _Installed_Files.txt
IF /I '%reloadAll%'=='T' echo. Installed Files: > c:\SCInc\ \_done\ _Installed_Files.txt
goto iviewerUrl
REM Creates the _done folder and the _Installed_Files.txt which will be local to the PC and will keep the products they install to prevent reinstallation
REM Delete this folder to reinstall all products or delete the line of text inside to reinstall a particular product

:iviewerUrl
set iviewerUrl=http://%websrvr%
IF /I '%SSL%'=='T' (set iviewerUrl=https://%websrvr%)
REM This sets the 'iviewerUrl' variable depeneding on if you have SSL enabled or not
set SCIncUrl=/bac/user/User_SO.asp?%OrgName=
REM This sets the rest of the URL for the browser shortcut (the '?' doesn't get a long with batch scripts)

:copy
REM Copies the files from the sharepath to the local machine
echo.
echo.Copying Necessary Files for Installation
echo.This could take a few minutes...
md C:\SCInc\ 
md C:\SCInc\Temp
REM Creates the ' \SCInc\ ' and ' \SCInc\Temp ' directories
goto 1

:1
verify >nul
findstr /x "copy_vcredist" "c:\SCInc\ \_done\ _Installed_Files.txt" 
IF '%ERRORLEVEL%'=='0' goto 2
echo.Copying vcredist_x86.exe
copy %sharepath%\vcredist_x86.exe c:\SCInc\temp
REM Copies the C++ Redistributable installer
IF '%ERRORLEVEL%'=='0' echo.copy_vcredist>> C:\SCInc\ \_done\ _Installed_Files.txt
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying vcredist_x86: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
goto 2
:2
verify >nul
findstr /x "copy_msxml6" "c:\SCInc\ \_done\ _Installed_Files.txt" 
IF '%ERRORLEVEL%'=='0' goto 3
echo.Copying msxml6.msi
copy %sharepath%\msxml6.msi c:\SCInc\temp
REM Copies MSXML 6.0 Parser installer
IF '%ERRORLEVEL%'=='0' echo.copy_msxml6>> C:\SCInc\ \_done\ _Installed_Files.txt
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying msxml6.msi: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
goto 3
:3
verify >nul
findstr /x "copy_BAC" "c:\SCInc\ \_done\ _Installed_Files.txt" 
IF '%ERRORLEVEL%'=='0' goto 4
echo.Copying  Browser Access Client Controls.msi
copy %sharepath%\" Browser Access Client Controls.msi" C:\SCInc\temp
REM Copies the  Browser Access Client Controls installer
IF '%ERRORLEVEL%'=='0' echo.copy_BAC>> C:\SCInc\ \_done\ _Installed_Files.txt
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying  Browser Access Client Controls.msi: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
goto 4
:4
verify >nul
findstr /x "copy_AutoUpdate" "c:\SCInc\ \_done\ _Installed_Files.txt" 
IF '%ERRORLEVEL%'=='0' goto 5
echo.Copying  Automated Update Service.msi
copy %sharepath%\" Automated Update Service.msi" C:\SCInc\temp
REM Copies the  Automated Update Service installer
IF '%ERRORLEVEL%'=='0' echo.copy_AutoUpdate>> C:\SCInc\ \_done\ _Installed_Files.txt
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying  Automated Update Service.msi: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
goto 5
:5
verify >nul
findstr /x "copy_icon" "c:\SCInc\ \_done\ _Installed_Files.txt" 
IF '%ERRORLEVEL%'=='0' goto copy_dotnet35
echo.Copying  BAC Icon
copy %sharepath%\bac.ico %windir%\System32
REM Copies the  Browser icon file to the System32 folder
IF '%ERRORLEVEL%'=='0' echo.copy_icon>> C:\SCInc\ \_done\ _Installed_Files.txt
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying bac.ico: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
echo.
echo.Created the following directories:
echo.-------------------------------------------------------
echo.
echo.C:\SCInc\ 
echo.C:\SCInc\Temp
echo.
echo.-------------------------------------------------------
goto copy_dotnet35

:copy_dotnet35
verify >nul
REM Copies .NET 3.5 Service Pack 1
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5"| findstr Install
REM Checks for .NET 3.5 and installs it if it doesn't find it
IF '%ERRORLEVEL%'=='0' set dotnet35=F
IF /I '%dotnet35%'=='F' goto copy_dotnet40
echo.Copying and installing .NET 3.5 Service Pack 1
echo.This will take a few minutes...
copy %sharepath%\dotnetfx35.exe C:\SCInc\Temp
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying .NET 3.5 SP1: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1

:dotnet35
verify >nul
REM Installs .NET 3.5 Service Pack 1
echo.Installing .NET 3.5 Service Pack 1
echo.This could take a few minutes...
c:\SCInc\temp\dotnetfx35.exe /q /norestart
IF '%ERRORLEVEL%'=='0' echo.dotnet35>> C:\SCInc\ \_done\ _Installed_Files.txt & set net35=1
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error completing the installation of .NET 3.5 SP1: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
echo.
echo.-------------------------------------------------------
echo.
goto copy_dotnet40

:copy_dotnet40
verify >nul
REM Copies .NET 4.0
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"| findstr Install
REM Checks for the FULL (not client) version of .NET 4.0 and installs it if it doesn't find it
IF '%ERRORLEVEL%'=='0' set dotnet40=F
IF /I '%dotnet40%'=='F' goto msxml60
echo.Copying and installing .NET 4.0
echo.This could take a few minutes...
copy %sharepath%\dotNetFx40_Full_x86_x64.exe c:\SCInc\temp
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error copying .NET 4: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1

:dotnet40
verify >nul
REM Installs .NET 4.0
echo.Installing .NET 4.0
echo.This could take a few minutes...
c:\SCInc\temp\dotNetFx40_Full_x86_x64.exe /q /norestart
IF '%ERRORLEVEL%'=='0' echo.dotnet40>> C:\SCInc\ \_done\ _Installed_Files.txt & set net40=1
IF NOT '%ERRORLEVEL%'=='0' echo.%date% - %time% - %computername% - There was an error completing the installation of .NET 4: Error Code %errorlevel%>> %sharepath%\%org%_ _Installed_PCs_ErrorLog.txt & set cleanInstall=1
goto msxml60

上面的脚本是部分的并且已经被编辑过了。

正如你所看到的,我在文件的开头设置了所有的变量。这样每次我需要改变一些东西时,我就不必去修改代码,我的同事也是如此。他们中的一些人有能力更改它,而有些人则很容易理解要更改的内容,因为所有的内容都在文件的前70行中设置。 我认为这是最好的方法,因为我有灵活性来随时更改事物。我有一些变量触发其他安装,我可能想稍后进行分阶段。使用创建另一个脚本的脚本,您可能会发现自己缺乏在不重新运行第一个批处理文件的情况下更改事物的能力。

@echo off
> usermessage.vbs ECHO WScript.Echo InputBox( "What is the name of the Service you want to restart?", "Service:", "MspSvc" )
FOR /F "tokens=*" %%A IN ('CSCRIPT.EXE //NoLogo usermessage.vbs') DO SET Service=%%A
ECHO You will restart %Service%
DEL usermessage.vbs

net stop %Service%
net start %Service%

Pause

这是来自Rob Vanderwoude的内容。他拥有我所知道的最常用的批处理脚本命令网站。我修改了这个片段,以接受用户输入并使用它来编写我的变量以重新启动服务。如果您想编写一个编写另一个脚本的脚本,那么这可能适用于您想要做的事情。

如果是这种情况,那么您需要做的是:

@ECHO off
TITLE Script Creator
COLOR 0A
SETLOCAL

> usermessage.vbs echo.WScript.Echo InputBox( "Pick something to test SETLOCAL/ENDLOCAL on?", "Variable:", "OUTSIDEVARIABLE" )
FOR /F "tokens=*" %%A IN ('CSCRIPT.EXE //NoLogo usermessage.vbs') DO SET outsideVar=%%A
echo.Your variable is %outsideVar%
DEL usermessage.vbs

echo.%outsideVar% is still set outside

echo.@echo off>c:\temp\newbatch.bat
echo.COLOR 1B>>c:\temp\newbatch.bat
echo.SETLOCAL>>c:\temp\newbatch.bat
echo.cls>>c:\temp\newbatch.bat
echo.set insideVar=%%outsideVar%% >>c:\temp\newbatch.bat
echo.^> usermessage.vbs echo.WScript.Echo InputBox( "Pick something to test SETLOCAL/ENDLOCAL on?", "Variable:", "INSIDEVARIABLE" ) >>c:\temp\newbatch.bat
echo.FOR /F "tokens=*" %%%%A IN ('CSCRIPT.EXE //NoLogo usermessage.vbs') DO SET insideVar=%%%%A >>c:\temp\newbatch.bat
echo.DEL usermessage.vbs >>c:\temp\newbatch.bat
echo.echo.Your variable is %%insideVar%% >>c:\temp\newbatch.bat
echo.echo.You have set the Second Variable: %%insideVar%% in this new batch file! >>c:\temp\newbatch.bat
echo.echo.%%insideVar%% is now Inside the new batch >>c:\temp\newbatch.bat
echo.Pause >>c:\temp\newbatch.bat
echo.ENDLOCAL >>c:\temp\newbatch.bat

echo.You are about to run the new batch file
pause
call c:\temp\newbatch.bat

cls
pause
echo.You created a batch file that set your outsideVar variable in it
echo.%outsideVar% is still set outside

ENDLOCAL
REM Install Batch Script
REM Created by Trevor G.

在这个脚本中,我们有一种独特的方法来确保每一行都会被写入到新批处理脚本的末尾,但是你需要记住关键的事情...有时候你需要使用^(插入符号),有时候你需要双倍使用%%来确保你的变量都能够传递,并且在将其写入新脚本之前不会触发另一个命令。

你的脚本太长了。请将示例减少到相关的子部分以显示您的答案。您不需要整个脚本。此外,请勿发布另一个答案作为续篇,这被视为噪音,并将被删除。如果您无法将其放入一个答案中,则这是您已经说得太多的很好的指示。 - casperOne
3
我们当然希望你能提供帮助,但我们也希望每个人都追求一定的质量水平,这有助于帮助每个人。话虽如此,并不是说回答是“糟糕”的,而是可以使其更好,我们总是希望SO的用户们能够“改进事物”。看看你的回答,找出真正所需的内容(而不是一个代码转储,其中有很多无关的部分),并将其简化;你会发现它将得到更好的接受(以点赞的形式)。 - casperOne
我非常感谢您的评论并尊重您所做的事情,我已经在您指出我的错误后几分钟内进行了更改,并希望现在能够将重点放在我为提问者提供的工作上。我希望下一个回复能够集中于实际问题。 - rud3y
我不确定为什么两个脚本的错误检查会成为一个大问题。一旦生成脚本和模板脚本完成,需要进行的唯一错误检查是稍后由脚本自动生成的变量。这些变量只有少数,并且放置在最终脚本的最后。此外,这全部都是直接批处理吗?语法看起来很陌生。 - Iszi
我的帖子的后半部分使用批处理中的wscript,但可以轻松地设置/setp变量=:并等待用户输入。我认为检查错误是一个好习惯,但你指的是哪一部分? - rud3y
显示剩余2条评论

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