写了可执行文件启动器Launcher.jar及一些批处理,通过它们就可以自动的以一定的时间间隔提取Hprof和进程的内存信息;
一、需要的库
可执行文件启动器:lib\Launcher.jar
注:关于Launcher.jar的源码如下:
源码包含2个文件Worker.java和Launcher.java
Worker.java文件:
package com.teleca.robin;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class Worker extends Thread {
private boolean loop=true;
private boolean paused=false;
private int cnt=0;
final private JLabel consoleText;
Worker(JLabel lable)
{
consoleText=lable;
}
private long interval=1000;
void setInterval(long interval)
{
this.interval=interval;
}
private String executableFileName;
void setExecutableFileName(String file)
{
if(executableFileName!=null&&file!=null)
{
if(!executableFileName.equals(file))
cnt=0;
}
executableFileName=file;
}
public void doPause()
{
paused=true;
}
public void doResume()
{
paused=false;
interrupt();
}
public void die()
{
loop=false;
}
public void run()
{
while(loop)
{
if(paused||executableFileName==null)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
else
{
BufferedReader stdout = null;
try {
Process p = null;
String line = null;
p = Runtime.getRuntime().exec(executableFileName, null, null);
stdout = new BufferedReader(new InputStreamReader(p
.getInputStream()));
while ((line = stdout.readLine()) != null) {
System.out.println(line);
}
stdout.close();
stdout=null;
} catch (IOException e) {
e.printStackTrace();
}finally{
if(stdout!=null)
try {
stdout.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
cnt++;
if(consoleText!=null)
consoleText.setText("Execute the file "+cnt+" times");
try {
Thread.sleep(interval);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
}
}
System.out.println("exit!");
}
}
源码文件2:Launcher.java
package com.teleca.robin;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
public class Launcher extends JFrame implements WindowListener{
private long interval;
private String executableFileName;
Launcher(String file,long time)
{
if(file==null)
executableFileName="";
else
executableFileName=file;
interval=time;
initComponents();
this.addWindowListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jButtonStart = new javax.swing.JButton();
jButtonPause = new javax.swing.JButton();
jButtonExit = new javax.swing.JButton();
jTextFieldFile = new javax.swing.JTextField(20);
jTextFieldTime = new javax.swing.JTextField(5);
jLabelFile = new javax.swing.JLabel();
jLabelTime = new javax.swing.JLabel();
jLabelTip = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setName("Form"); // NOI18N
jButtonStart.setText("Start"); // NOI18N
jButtonStart.setName("jButtonStart"); // NOI18N
jButtonStart.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
start();
}
});
jButtonPause.setText("Pause"); // NOI18N
jButtonPause.setName("jButtonPause"); // NOI18N
jButtonPause.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pause();
}
});
jButtonExit.setText("Exit"); // NOI18N
jButtonExit.setName("jButtonExit"); // NOI18N
jButtonExit.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stop();
System.exit(0);
}
});
jButtonStart.setEnabled(true);
jButtonPause.setEnabled(false);
jTextFieldFile.setText(executableFileName); // NOI18N
jTextFieldFile.setName("jTextFieldFile"); // NOI18N
jTextFieldFile.setHorizontalAlignment(JTextField.CENTER);
jTextFieldTime.setText(""+interval); // NOI18N
jTextFieldTime.setName("jTextFieldTime"); // NOI18N
jTextFieldTime.setHorizontalAlignment(JTextField.CENTER);
jLabelFile.setText("the executable file"); // NOI18N
jLabelFile.setName("jLabelFile"); // NOI18N
jLabelTime.setText("the interval time(millisecond)"); // NOI18N
jLabelTime.setName("jLabelTime"); // NOI18N
jLabelTip.setText("idle"); // NOI18N
jLabelTip.setName("jLabelTip"); // NOI18N
jLabelTip.setHorizontalAlignment(SwingConstants.CENTER);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(71, 71, 71)
.addComponent(jLabelTime))
.addGroup(layout.createSequentialGroup()
.addGap(70, 70, 70)
.addComponent(jLabelFile))
.addGroup(layout.createSequentialGroup()
.addGap(90, 90, 90)
.addComponent(jButtonStart)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(100, 100, 100)
.addComponent(jTextFieldTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(jTextFieldFile, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
)
.addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(jButtonPause)
)
.addGroup(layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(jLabelTip)
)
.addGroup(layout.createSequentialGroup()
.addGap(180, 180, 180)
.addComponent(jButtonExit)
)
))
)
.addContainerGap(151, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(63, 63, 63)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabelFile)
.addComponent(jTextFieldFile, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(29, 29, 29)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabelTime)
.addComponent(jTextFieldTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(62, 62, 62)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButtonStart)
.addComponent(jButtonPause)
.addComponent(jButtonExit))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 34, Short.MAX_VALUE)
.addComponent(jLabelTip)
.addGap(32, 32, 32))
);
pack();
}// </editor-fold>//GEN-END:initComponents
Worker worker;
private void stop() {//GEN-FIRST:event_stop
// TODO add your handling code here:
if(worker!=null)
{
worker.die();
worker=null;
}
}//GEN-LAST:event_stop
private void pause() {//GEN-FIRST:event_pause
// TODO add your handling code here:
worker.doPause();
jButtonStart.setEnabled(true);
jButtonPause.setEnabled(false);
jTextFieldFile.setEditable(true);
jTextFieldTime.setEditable(true);
}//GEN-LAST:event_pause
private void start() {//GEN-FIRST:event_start
// TODO add your handling code here:
String fileName=jTextFieldFile.getText().trim();
if(checkFileName(fileName))
{
executableFileName=fileName;
}
else
{
return;
}
String time=jTextFieldTime.getText();
if(time.length()==0)
{
interval=defaultInterval;
}
else
{
try{
long t=Long.parseLong(time);
interval=t;
}catch(NumberFormatException e)
{
jLabelTip.setText("the time "+time+"is not correct!");
return;
}
}
if(worker==null)
{
worker=new Worker(this.jLabelTip);
worker.setExecutableFileName(executableFileName);
worker.setInterval(interval);
worker.start();
jLabelTip.setText("start to execute file");
}
else
{
worker.setExecutableFileName(executableFileName);
worker.setInterval(interval);
worker.doResume();
jLabelTip.setText("Restart to execute file");
}
jButtonStart.setEnabled(false);
jButtonPause.setEnabled(true);
jTextFieldFile.setEditable(false);
jTextFieldTime.setEditable(false);
}//GEN-LAST:event_start
final static long defaultInterval=1000;
/**
* @param args
*/
public static void main(String[] args) {
String fileName="";
long time=defaultInterval;
if(args.length>0)
{
fileName=args[0];
if(!checkFileName(fileName))
{
fileName="";
}
}
if(args.length>1)
{
time=Long.parseLong(args[1]);
}
// TODO Auto-generated method stub
new Launcher(fileName,time).setVisible(true);
}
static boolean checkFileName(String fileName)
{
if(fileName==null)
{
return false;
}
if(!(fileName.endsWith(".exe")||fileName.endsWith(".bat")||fileName.endsWith(".cmd")))
{
System.out.println("the file must be a executable file!");
System.out.println("Now only support *.exe , *.bat,*.cmd");
System.out.println("Please check the file again");
return false;
}
File file=new File(fileName);
if(!file.exists())
{
System.out.println("the file:"+fileName+" is not exists");
return false;
}
return true;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton jButtonExit;
private javax.swing.JButton jButtonPause;
private javax.swing.JButton jButtonStart;
private javax.swing.JLabel jLabelFile;
private javax.swing.JLabel jLabelTime;
private javax.swing.JLabel jLabelTip;
private javax.swing.JTextField jTextFieldFile;
private javax.swing.JTextField jTextFieldTime;
// End of variables declaration//GEN-END:variables
public void windowDeactivated(WindowEvent e)
{
//System.out.println("window is deactivated");
}
public void windowDeiconified(WindowEvent e)
{
//System.out.println("window is Deiconified");
}
public void windowActivated(WindowEvent e)
{
//System.out.println("window is actived");
}
public void windowOpened(WindowEvent e)
{
//System.out.println("window is Opened");
}
public void windowClosing(WindowEvent e)
{
stop();
}
public void windowClosed(WindowEvent e)
{
//System.out.println("window is Closed");
}
public void windowIconified(WindowEvent e)
{
//System.out.println("window is Closing");
}
}
用源代码生成的jar文件为:Launcher.jar。
则可以用如下的DOS命令运行它:
java -jar lib\Launcher.jar getProcessState.bat 5000
5000表示每隔5000毫秒执行一次getProcessState.bat 文件。
二、自动提取Hprof。
runHprofPicker.bat文件:
java -jar lib\Launcher.jar getHprof.bat 5000
5000表示5000毫秒执行一次 getHprof.bat来从手机提取Hprof文件。
getHprof.bat文件如下:
@echo off
call config.bat
if exist %hpInputFile% (
del %hpInputFile% /q
)
adb pull %hpInputFileDir%/%hpInputFile% .
if not exist %hpInputFile% (
echo fail to pull %hpInputFile%
exit 1
)
if not exist %hpRoot% (
md %hpRoot%
)
Setlocal enabledelayedexpansion
set path=%path%;%cd%\lib
call genSerial
set serial=!genSerial~result!
set hpOutFile=%serial%.hprof
%tools%\hprof-conv.exe %hpInputFile% %hpRoot%\%hpOutFile%
echo success!
endlocal
前提:
1生成批处理配置文件config.bat:
批处理配置文件config.bat如下:
rem the following var is for getProcessState.bat
set rawDatadir=rawData
set processName=android.process.acore;com.android.systemui
rem set processShortName=a;b;c
set processShortName=
set outRoot=out
set statFilePrefix=stat
rem the following var is for getHprof.bat
set tools=D:\SDK\android-sdk4.0\tools
set hpInputFileDir=/sdcard
set hpInputFile=input.hprof
set hpRoot=hpTemp
注1:rawDatadir为“ps -x”提取出来的文件的目录
注2:processName需要统计RSS的进程的名字,可以同时统计多个,进程名之间用“;”进行分割。
注3:processShortName需要统计RSS的进程的名字的缩写形式,如果不坐设置或设置为空,这程序会根据processName自动生成。
注4:outRoot为对进程的RSS进行统计最后的生成文件的存放目录。
注5:statFilePrefix为对进程的RSS进行统计最后的生成文件的前缀。
注6:tools为hprof-conv.exe所在的目录。
注7:hpInputFileDir为手机中我们生成的hprof文件所在的目录。
注8:hpInputFileDir为手机中我们生成的hprof文件的名字。
2.在代码中生成Hprof文件
在android代码,可以使用如下代码把hprof文件生成到sd卡上。
Debug.dumpHprofData("/sdcard/input.hprof");
可以不用sd卡,而将hprof文件直接生成在手机上,但是只能在"/data/data/"+packageName的目录下。
实例1:
void generateHprof()
{
String packageName=getApplicationInfo().packageName;
String hpFilePath="/data/data/"+packageName+"/input.hprof";
try {
//Debug.dumpHprofData("/sdcard/input.hprof");
Debug.dumpHprofData(hpFilePath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
注1:需要在代码中先生成hprof文件,而且config.bat中配置的路径要和在代码中先生成hprof文件的路径一致。
注
2:需要把bat库genSerial.bat,getSubStr.bat放在子目录lib中。(如下实例)
getSubStr.bat源码
@echo off
set %0~string=%string%
set %0~position=%position%
set %0~count=%count%
set %0~result=
set string=%1
set /a position=%2
set /a count=0
set string=%string:"=%
if %position% LSS 0 (
for %%i in (%string%) do (
set /a count=!count!+1
)
set /a position=%position%+!count!
)
set /a count=0
for %%i in (%string%) do (
if !count! EQU %position% (
set %0~result=%%i
)
set /a count=!count!+1
)
set string=!%0~string!
set position=!%0~position!
set count=!%0~count!
使用实例1:
@echo off
Setlocal enabledelayedexpansion
set path=%path%;%cd%\lib
call getSubStr "a,b,c,d" 0
set result=!getSubStr~result!
echo the first subString is %result%
call getSubStr "a;b;c;d" 1
set result=!getSubStr~result!
echo the second subString is %result%
call getSubStr "a b c d" -1
set result=!getSubStr~result!
echo the firest subString from right is %result%
pause
genSerial.bat文件
@echo off
set %0~SNFile=%SNFile%
set %0~SN=%SN%
set %0~result=
set SNFile=SN.txt
if "%1" =="reset" (
set /a SN=0
goto save
)
if exist %SNFile% (
for /f %%i in (%SNFile%) do set /a SN=%%i
set /a SN+=1
) else (
set /a SN=0
)
:save
echo %SN% >%SNFile%
set %0~result=%SN%
set SNFile=!%0~SNFile!
set SN=!%0~SN!
使用实例1:
@echo off
Setlocal enabledelayedexpansion
set path=%path%;%cd%\lib
call genSerial
set serial=%genSerial~result%
echo the serial is %serial%
call genSerial
set serial=!genSerial~result!
echo the serial is %serial%
call genSerial reset
set serial=!genSerial~result!
echo after reset,the serial is %serial%
pause
三、自动提取进程的RSS
runProcessStatePicker.bat文件:
java -jar lib\Launcher.jar getProcessState.bat 5000
5000表示5000毫秒执行一次 getProcessState.bat来从手机提取进程的RSS,输出到一个以进程名命名的文本文件,以便在excel中生产图表文件。
前言:RSS(Resident Set Size)表示实际使用物理内存(包含共享库占用的内存)。
因为它包含了共享内存,所以他的意义不及PSS。所以现在查看内存使用情况一般参照PSS。
提取进程的RSS
文件:getProcessState.bat
@echo off
call config.bat
set processNameCol=9
set RSSCol=5
Setlocal enabledelayedexpansion
set path=%path%;%cd%\lib
set /a processNo=0
for %%i in (%processName%) do (
set /a processNo+=1
)
set /a cnt=0
set str=
if "%processShortName%"=="" (
set /a cnt=0
for %%i in (%processName%) do (
set str=%%i
set str=!str:.= !
call getSubStr "!str!" -1
set result=!getSubStr~result!
if !cnt! EQU 0 (
set processShortName=!result!
) else (
set processShortName=!processShortName!;!result!
)
set /a cnt+=1
)
) else (
set /a cnt=0
for %%i in (%processShortName%) do (
set /a cnt=!cnt!+1
)
if not !cnt! EQU %processNo% (
echo the number of process Name is %processNo%
echo the number of process Short Name is !cnt!
echo they should be equal.Please check again!
exit 0
)
)
if not exist %rawDatadir% (
md %rawDatadir%
)
if not exist %outRoot% (
md %outRoot%
)
call genSerial
set serial=!genSerial~result!
set psFile=%rawDatadir%\ps%serial%.txt
adb shell "ps -x" >%psFile%
adb shell exit
set /a cnt=0
FOR /F "skip=1 tokens=%RSSCol%,%processNameCol%" %%i in (%psFile%) do (
set /a cnt=0
for %%a in (%processName%) do (
if "%%a"=="%%j" (
call getSubStr "%processShortName%" !cnt!
set result=!getSubStr~result!
set statFile=%outRoot%\%statFilePrefix%_!result!.txt
if exist !statFile! (
>>!statFile! set/p=,%%i<nul
) else (
>>!statFile! set/p=%%i<nul
)
)
set /a cnt=!cnt!+1
)
)
echo success!
endlocal
四、自动提取进程的NativeSize,DalvikSize,NativeAllocated,DalvikAllocated,NativePSS,DalvikPSS
runMemoryPicker.bat文件:
java -jar lib\Launcher.jar getMemoryState.bat 2000
2000表示2000毫秒执行一次 getMemoryState.bat来从手机提取进程的内存信息到一个以进程名命名的文本文件,以便在excel中生产图表文件。
前言:
我们可以通过adb shell "dumpsys meminfo %curProcessName%"命令得到某个进程的内存使用情况。
getMemoryState.bat主要用于解析该命令返回的内存,得到NativeSize,DalvikSize,NativeAllocated,DalvikAllocated,NativePSS,DalvikPSS等。它把不同时期得到的数据按时间前后为顺序以逗号作为分隔符的排列输出到一个以进程名命名的文本文件,以便在excel中生产图表。
getMemoryState.bat文件源码
@echo off
call config.bat
set typeNameCol=1
set nativeValueCol=2
set dalvikValueCol=3
set dalvikOtherCol=4
Setlocal enabledelayedexpansion
set path=%path%;%cd%\lib
set /a processNo=0
for %%i in (%processName%) do (
set /a processNo+=1
)
set /a cnt=0
set str=
if "%processShortName%"=="" (
set /a cnt=0
for %%i in (%processName%) do (
set str=%%i
set str=!str:.= !
call getSubStr "!str!" -1
set result=!getSubStr~result!
if !cnt! EQU 0 (
set processShortName=!result!
) else (
set processShortName=!processShortName!;!result!
)
set /a cnt+=1
)
) else (
set /a cnt=0
for %%i in (%processShortName%) do (
set /a cnt=!cnt!+1
)
if not !cnt! EQU %processNo% (
echo the number of process Name is %processNo%
echo the number of process Short Name is !cnt!
echo they should be equal.Please check again!
exit 0
)
)
if not exist %rawDatadir% (
md %rawDatadir%
)
if not exist %outRoot% (
md %outRoot%
)
call genSerial
set serial=!genSerial~result!
set meminfoFile=%rawDatadir%\meminfo%serial%.txt
set slipChar=,
:Loop0Start:
set /a processTotalNumber=cnt
set /a processNo=0
:Loop0Head
call getSubStr "%processShortName%" %processNo%
set curProcessShortName=%getSubStr~result%
call getSubStr "%processName%" %processNo%
set curProcessName=%getSubStr~result%
:GetRawMemInfoFile
set statFile=%outRoot%\%statFilePrefix%_Memory_%curProcessShortName%.txt
adb shell "dumpsys meminfo %curProcessName%" >%meminfoFile%
if not exist %statFile% (
set slipChar=
goto ReadRawMemInfoFile
)
:ReadMemInfoStatisticFile
set NativeSize=
set DalvikSize=
set OtherSize=
set TotalSize=
set NativeAllocated=
set DalvikAllocated=
set OtherAllocated=
set TotalAllocated=
set NativePSS=
set DalvikPSS=
set OtherPSS=
set TotalPSS=
set NativeFree=
set DalvikFree=
set OtherFree=
set TotalFree=
set /a cnt=0
FOR /F "delims=: tokens=2" %%i in (%statFile%) do (
if !cnt! EQU 0 (
set NativeSize=%%i
)
if !cnt! EQU 1 (
set DalvikSize=%%i
)
if !cnt! EQU 2 (
set NativeAllocated=%%i
)
if !cnt! EQU 3 (
set DalvikAllocated=%%i
)
if !cnt! EQU 4 (
set NativePSS=%%i
)
if !cnt! EQU 5 (
set DalvikPSS=%%i
)
set /a cnt+=1
)
:ReadRawMemInfoFile
set /a cnt=0
FOR /F "skip=1 tokens=1,2,3,4,5,6,7,8" %%i in (%meminfoFile%) do (
if !cnt! EQU 4 (
set NativeSize=!NativeSize!%slipChar%%%j
set DalvikSize=!DalvikSize!%slipChar%%%k
set OtherSize=!OtherSize!%slipChar%%%l
set TotalSize=!TotalSize!%slipChar%%%m
)
if !cnt! EQU 5 (
set NativeAllocated=!NativeAllocated!%slipChar%%%j
set DalvikAllocated=!DalvikAllocated!%slipChar%%%k
set OtherAllocated=!OtherAllocated!%slipChar%%%l
set TotalAllocated=!TotalAllocated!%slipChar%%%m
)
if !cnt! EQU 6 (
set NativeFree=!NativeFree!%slipChar%%%j
set DalvikFree=!DalvikFree!%slipChar%%%k
set OtherFree=!OtherFree!%slipChar%%%l
set TotalFree=!TotalFree!%slipChar%%%m
)
if !cnt! EQU 7 (
set NativePSS=!NativePSS!%slipChar%%%k
set DalvikPSS=!DalvikPSS!%slipChar%%%l
set OtherPSS=!OtherPSS!%slipChar%%%m
set TotalPSS=!TotalPSS!%slipChar%%%n
)
set /a cnt+=1
)
:SaveToMemInfoStatisticFile
echo NativeSize:%NativeSize%>%statFile%
echo DalvikSize:%DalvikSize%>>%statFile%
echo NativeAllocated:%NativeAllocated%>>%statFile%
echo DalvikAllocated:%DalvikAllocated%>>%statFile%
echo NativePSS:%NativePSS%>>%statFile%
echo DalvikPSS:%DalvikPSS%>>%statFile%
:Loop0Tail
set /a processNo+=1
if %processNo% LSS %processTotalNumber% (
goto Loop0Head
)
:Loop0End
echo success!
endlocal
本文来自:http://hubingforever.blog.163.com/blog/static/1710405792011324114317115/