1. Background
I have a batch script as follows:
@echo off
chcp 936 & cls
cd /D F:\Chen\python3\ExciseC
set fdate=%date:~0,4%%date:~5,2%%date:~8,2%
python main.py >> crawl_record_%fdate%.log 2>&1
for /F %%f in ('dir crawl_record_*.log /B ^| find /V "%fdate%"') do move %%f archived\logs
Among them, for /F
the statement is to move the log files other than the current day archived\logs
, and then this batch is run regularly in the task plan.
After running for a period of time, I occasionally found that there were still log files outside the current day that had not been moved archived\logs
. Later, I found that the file was python.exe
occupied by the program. It is speculated that python main.py >> crawl_record_%fdate%.log 2>&1
this statement may end abnormally during the execution process (such as restart, disconnection, etc.) network, etc.), causing the file after the redirection character to be locked, and then the file could not be moved normally on the second day.
Then my demand appeared: find the process that occupies this file, then kill it, release the occupied file, and then move the file. (These steps are all done in batch processing, not graphical interface operations)
Two, solve the problem
1. Find the process pid occupying the specified file
The command program is used here handle.exe
, handle.exe
which is Sysinternals Suite
the process utility in .
It can be used to display information about open handles for any process in the system. You can use this to see which program has a file open, or to see the object types and names of all handles to a program.
Help document: https://learn.microsoft.com/en-us/sysinternals/downloads/handle
Download address: https://download.sysinternals.com/files/Handle.zip
Download and unzip the handle.exe
/ handle64.exe
application, then put It is placed under a certain path in the path environment variable.
handle /v name /nobanner
/v
Make the output result in csv format with comma delimiter, name
the name of the file opened by the process to search (accept fragment), /nobanner
do not display startup banner and copyright information. See handle /?
help for more information.
The output structure is as follows:
C:\Users\cyinl>handle /v /nobanner crawl_record_20230722.log
Process,PID,Type,Handle,Name
python.exe,23156,File,0x000001E0,F:\test\crawl_record_20230722.log
Note:
tasklist
You can also check the process pid, but it can only be searched according to the exe/dll module name, the command format is:
tasklist /M 模块名
, the file like the example in the examplecrawl_record_20230722.log
cannot find the relevant process.
2. Terminate the process
handle.exe
The process can be terminated, the format is:
handle /nobanner /p PID /c <句柄号>
However, the handle number is not easy to get, as follows:
C:\Users\cyinl>handle /nobanner /p 23156
40: File D:\Chen\MySoft\Python\Python3.7.7
1D0: File C:\Windows\System32\zh-CN\KernelBase.dll.mui
1E0: File F:\test\crawl_record_20230722.log
208: File C:\Windows\System32\zh-CN\kernel32.dll.mui
The 40 in the first line should be the handle number of the process file to be closed, but can you ensure that the handle number in the first line is to be closed? ? ? Somewhat confused.
taskkill
The command can also terminate the process, the format is:
taskkill /f /pid <进程号>
/f
Indicates that the process is forcibly terminated, /pid
specifying the pid process number of the process to be terminated.
The output results are as follows:
C:\Users\cyinl>taskkill /f /pid 23156
成功: 已终止 PID 为 23156 的进程。
3. Implement the code
There are handle
two taskkill
commands to improve batch processing to solve the problem. The specific implementation is as follows:
@echo off
chcp 936 & cls
cd /D F:\Chen\python3\ExciseC
set fdate=%date:~0,4%%date:~5,2%%date:~8,2%
python main.py >> crawl_record_%fdate%.log 2>&1
for /F %%f in ('dir crawl_record_*.log /B ^| find /V "%fdate%"') do move %%f archived\logs
REM After the above command is executed, if the files are successfully moved, they will not enter the loop of this command
for /F %%f in ('dir crawl_record_*.log /B ^| find /V "%fdate%"') do (
echo the file to move is locked: %%f
for /F "skip=1 tokens=2 delims=," %%p in ('handle /v %%f /nobanner') do taskkill /F /PID %%p
REM If the move command is executed immediately after the process is deleted, it will fail. Therefore, a 5-second delay is given here
timeout /T 5 /nobreak > nul
move %%f archived\logs
)
explain:
- If the first one
for /F
can move files normally, it will not enter the second onefor /F
;- The second one
for /F
is used to find the occupied log file, and the third one is nested tofor /F
get the process pid, whichskip
means to skip the first row,delims=,
means to,
separate the data by columns, andtokens=2
means to take the second column. Thendo
use the command in the structuretaskkill
to terminate the process corresponding to the found pid.- The test found that after the process is terminated, the file cannot be moved immediately, otherwise it may fail, so the
timeout
command is used to delay for 5 seconds before moving the file.
3. Expansion
Find and kill processes manually
1. Use the system's own资源监视器
1) Search in the taskbar 资源监视器
, switch to CPU
the tab, and enter the occupied file name in the 关联的句柄
- input box; 2) In the search results, select the process that needs to be terminated, right click, and select搜索句柄
终止进程
2. Use Process Explorer
tools
You can also use Process Explorer , which is handle
a GUI-based version of Windows, Process Explorer
to view information about which handles and DLLs a process has open or loaded.
Download address: https://download.sysinternals.com/files/ProcessExplorer.zip
After decompression, open procexp.exe
/ procexp64.exe
use it directly.
1) Click 搜索
the icon and enter the name of the file to be searched;
2) In the search results, click the search result, and the relevant process and file handle will be automatically located;
3) Right-click the corresponding process- kill Process
or right-click the corresponding file handle-close Handle