创build唯一的文件名Windows批处理

我已经看到很多关于创build一个独特的文件名从天真%TIME%到合理(但不足)%RANDOM%的文章。 使用wmic os get localdatetime要好得多,但是它仍然可能在多个CPU /核心机器上失败。 在多核心机器上运行5个以上的shell时,以下脚本最终将失败。

 @ECHO OFF SETLOCAL ENABLEDELAYEDEXPANSION FOR /L %%i IN (0, 1, 1000) DO ( FOR /F "usebackq" %%x IN (`wmic os get localdatetime ^| find "."`) do (set MYDATE=%%x) ECHO MYDATE is now !MYDATE! IF EXIST testuniq_!MYDATE!.txt ( ECHO FAILED ON !MYDATE! GOTO TheEnd ) COPY NUL >testuniq_!MYDATE!.txt ) :TheEnd EXIT /B 0 

有没有人有一个可靠的方法来创build一个shell脚本中的唯一文件名?

Solutions Collecting From Web of "创build唯一的文件名Windows批处理"

任何依赖随机数的系统在理论上都可能失败,即使是使用GUID的系统也是如此。 但是,世界似乎已经接受GUID是足够好的。 我看过一些使用CSCRIPT JScript生成GUID的代码。

还有其他的方法:

首先,我将假定您正在尝试创建一个唯一的临时文件。 由于该文件将在您的进程结束后被删除,因此您只需要在名称是临时文件名的衍生物的资源上建立排它锁。 (实际上我反过来 – 临时名称来自锁定的资源名称)。

重定向会在输出文件上建立一个独占锁,所以我只是从时间(0.01秒预定义)中派生一个名字,并尝试在用户的临时文件夹中锁定一个具有该名字的文件。 如果失败了,我会循环再试,直到成功。 一旦我成功了,我保证拥有该锁的唯一所有权,以及所有衍生品(除非有人故意破坏系统)。

一旦我的进程终止,锁将被释放,同名的临时文件可以在以后重用。 但是脚本通常会在终止时删除临时文件。

 @echo off setlocal :getTemp set "lockFile=%temp%\%~nx0_%time::=.%.lock" set "tempFile=%lockFile%.temp" 9>&2 2>nul (2>&9 8>"%lockFile%" call :start %*) || goto :getTemp :: Cleanup 2>nul del "%lockFile%" "%tempFile%" exit /b :start :: Your code that needs the temp file goes here. This routine is called with the :: original parameters that were passed to the script. I'll simply write some :: data to the temp file and then TYPE the result. >"%tempFile%" echo The unique tempfile for this process is "%tempfile%" >>"%tempFile%" echo(%* type "%tempFile%" exit /b 

由于名称冲突导致的循环应该很少,除非你真的强调你的系统。 如果是这样,如果使用WMIC OS GET LOCALDATETIME而不是%TIME% ,则可以将循环的几率降低10倍。


如果你正在寻找一个持久的唯一名字,那么这个问题就更难一些,因为你无法无限地维护这个锁。 对于这种情况,我建议使用WMIC OS LocalDateTime方法,再加上两次名称冲突检查。

第一个检查只是验证文件不存在。 但是这是一个竞争条件 – 两个进程可以同时进行检查。 第二个检查创建文件(空),并建立一个临时排他锁。 诀窍是确保锁被维护的时间长于另一个进程检查文件是否存在的时间。 我很懒,所以我简单地使用TIMEOUT来建立1秒的等待方式。

getUniqueFile例程需要三个参数 – 一个基本名称,一个扩展名和要存储结果的变量名称。 基本名称可以包含驱动器和路径信息。 任何路径信息必须是有效的,否则例程将进入无限循环。 这个问题可以解决。

 @echo off setlocal :: Example usage call :getUniqueFile "d:\test\myFile" ".txt" myFile echo myFile="%myFile%" exit /b :getUniqueFile baseName extension rtnVar setlocal :getUniqueFileLoop for /f "skip=1" %%A in ('wmic os get localDateTime') do for %%B in (%%A) do set "rtn=%~1_%%B%~2" if exist "%rtn%" ( goto :getUniqueFileLoop ) else ( 2>nul >nul (9>"%rtn%" timeout /nobreak 1) || goto :getUniqueFileLoop ) endlocal & set "%~3=%rtn%" exit /b 

上面应该保证为给定路径返回一个新的唯一文件名。 在锁定检查期间建立一些要执行的命令有很多优化的空间,需要“足够长”而不是“太长”

下面的Batch-JScript混合脚本使用WSH的fso.GetTempName()方法,该方法专门为此目的而设计:

 @if (@CodeSection == @Batch) @then @echo off for /F "delims=" %%a in ('cscript //nologo //E:JScript "%~F0"') do set "fileName=%%a" echo Created file: "%fileName%" goto :EOF @end var fso = new ActiveXObject("Scripting.FileSystemObject"), fileName; do { fileName = fso.GetTempName(); } while ( fso.FileExists(fileName) ); fso.CreateTextFile(fileName).Close(); WScript.Echo(fileName); 

您可以使用certutil base64编码%date% %time%%random%种子是这样的:

 @echo off setlocal :: generate unique ID string >"%temp%\~%~n0.%username%.a" echo %username%%date%%time%%random% >NUL certutil -encode "%temp%\~%~n0.%username%.a" "%temp%\~%~n0.%username%.b" for /f "usebackq EOL=- delims==" %%I in ("%temp%\~%~n0.%username%.b") do set "unique_id=%%I" del "%temp%\~%~n0.%username%.a" "%temp%\~%~n0.%username%.b" echo %unique_id% 

如果同一个脚本是由多个用户在同一目录下运行的,我将%username%添加到临时文件中以避免更多的冲突。 我想你可以用%username%替换%random% %username%来达到同样的效果。 然后,如果一个用户同时执行两次相同的代码块,则只会发生冲突。

(编辑:添加%username%作为唯一性的种子。)

 @echo off :: generate a tempfilename in %1 (default TEMPFILE) :loop set /ay$$=%random%+100000 set y$$=temp%y$$:~1,2%.%y$$:~-3% if exist "%temp%\%y$$%" goto loop SET "y$$=%temp%\%y$$%"&copy nul "%temp%\%y$$%" >nul 2>NUL :: y$$ now has full tempfile name if "%1"=="" (set "tempfile=%y$$%") else (set "%1=%y$$%") 

下面是我的tempfile生成器的一个简化版本, tempnn.nnn%temp%目录中创建一个名为tempnn.nnn的文件。

一旦tempnn.nnn被创建,那么通过为%y$$%附加一个后缀,例如%y$$%.a等,创建许多进一步的临时文件是很简单%y$$%.a 。当然,这个假定某些其他进程不会随机创建文件名而不使用此过程。

使文件内容成为对象,使用指针memaddress作为文件名的第一部分,使用Rand()作为第二部分。 即使运行多个实例,内存地址对于所有对象也是唯一的。

我喜欢Aacini的JScript混合解决方案。 只要我们从其他运行时环境中借用,如何使用.NET的System.GUID与PowerShell?

 @echo off setlocal for /f "delims=" %%I in ('powershell -command "[string][guid]::NewGuid()"') do ( set "unique_id=%%I" ) echo %unique_id% 

很久以前,在一个叫做alt.msdos.batch的galax..newsgroup中,一个贡献者坚持要发布一个QBASIC解决方案给每个引发的问题,包装在一个批处理外壳中,声称这是批处理,因为它只使用标准的微软提供的软件。

很长一段时间后,他被错误的方法治好了,但从那以后,我一直对混合方法严重过敏。 当然,如果它能治愈这个问题,那么等等,而且有时候并没有逃脱那个过程(例如,默默地运行一批)。除此之外,我真的不想要学习一门新的语言或两门课程。这就是为什么我避开*脚本解决方案。 因人而异。

所以 – 这是另一种方法

 @ECHO OFF SETLOCAL :rndtitle SET "progtitle=My Batch File x%random%x" TITLE "%progtitle%" SET "underline=" SET "targetline=" FOR /f "delims=" %%a IN ('TASKLIST /v^|findstr /i /L /c:"======" /c:"%progtitle%"') DO ( IF DEFINED underline ( IF DEFINED targetline GOTO rndtitle SET "targetline=%%a" ) ELSE (SET "underline=%%a") ) :: Here be a trap. The first column expands to fit the longest image name... :pidloop IF "%underline:~0,1%"=="=" SET "underline=%underline:~1%"&SET "targetline=%targetline:~1%"&GOTO pidloop FOR /f %%a IN ("%targetline%") DO SET /a pid=%%a ECHO %pid% GOTO :EOF 

本质上,将标题设置为随机的东西。 请注意,使用了x%random%x ,而不是简单的%random%以便搜索“x123x”不会检测到“x1234x”。

接下来,得到一个详细的任务列表,找到标题和标题的下划线。 findstr返回的第一行将是下划线,下一行将设置targetline并且任何进一步的返回都表示标题不是唯一的,所以返回,更改并重试,直到它是唯一的。

最后,获取第二列中的进程ID,注意第一列的宽度是未知的(因此为什么下划线被抓取)。

结果:此进程的PID必须是唯一的,因此可以用作唯一临时文件名的基础 – 将其构建在为此目的而保留的目录中。

我想提交另一种方法,并找出是否有任何漏洞。 请告诉我。 谢谢。

 C:>title my shell a80en333xyb C:>type getppid.ps1 (gwmi win32_process -Filter "processid='$pid'").parentprocessid C:>powershell getppid.ps1 2380 

要验证是否找到了正确的任务,将标题设置为不可能的值,并使用tasklist.exe来搜索getppid.ps1返回的值。

 C:>tasklist /v | find "2380" cmd.exe 2380 RDP-Tcp#0 7 8,124 K Running PHSNT\pwatson 0:00:03 my shell a80en333xyb