从带有批处理文件的文件夹及其子文件夹中选择x个唯一的随机文件



我有一些批处理代码,可以从一个文件夹(及其子文件夹(中随机选择三个文件,但最终可能会多次选择同一个文件。我希望它总是选择唯一的文件。

它创建了一个包含所有选项的临时文本文件,所以我试图让它删除所选的行,并在每次选择一行时从文件总数中减去一行,这样它就从选项池中删除了。

以下是全部内容:

@echo off
setlocal
:: Create numbered list of files in a temporary file
set "tempFile=%temp%%~nx0_fileList_%time::=.%.txt"
set "tempFileTwo=%temp%%~nx0_fileList2_%time::=.%.txt"
pushd %1
dir /b /s /a-d *.mov *.mp4 | findstr /n "^" >"%tempFile%"
popd
:: Count the files
for /f %%N in ('type "%tempFile%" ^| find /c /v ""') do set cnt=%%N
for /l %%N in (1 1 3) do call :openRandomFile
:: Delete the temp files
del "%tempFile%"
del "%tempFileTwo%"
exit /b
:openRandomFile
set /a "randomNum=(%random% %% cnt) + 1"
for /f "tokens=1* delims=:" %%A in (
'findstr "^%randomNum%:" "%tempFile%"'
) do (
start "" "%%B"
echo Selection: %%B
set /a cnt -= 1
findstr /l /v "%%B" "%tempFile%" > "%tempFileTwo%"
copy /y "%tempFileTwo%" "%tempFile%"
)
exit /b

不起作用的部分是:

findstr /l /v "%%B" "%tempFile%" > "%tempFileTwo%"
copy /y "%tempFileTwo%" "%tempFile%"

我希望我的findstr能找到所选的文件并将其删除,然后复制其他所有文件,但它显然匹配了所有文件,并复制了一个完全空白的文件。不确定我做错了什么。

%RANDOM%数字可能会返回重复项,我们必须对此进行说明。当已经选择的文件必须返回时,您的代码的以下调整会重试选择随机文件:

@echo off
rem // Explicitly preset configuration:
setlocal EnableExtensions DisableDelayedExpansion
:: Create numbered list of files in a temporary file
set "tempFile=%temp%%~nx0_fileList_%time::=.%.txt"
set "tempFileTwo=%temp%%~nx0_fileList2_%time::=.%.txt"
rem // Improve quotation and regard failure if `%1` does not point to a directory:
pushd "%~1" && (
dir /b /s /a-d *.mov *.mp4 | findstr /n "^" > "%tempFile%"
popd
) || del "%tempFile%" 2> nul
:: Count the files
set "cnt=0" & for /f %%N in ('type "%tempFile%" ^| find /c /v ""') do set "cnt=%%N"
rem // Regard case when there are less than three files:
set "num=3" & if %cnt% lss 3 set "num=%cnt%"
for /l %%N in (1 1 %num%) do call :openRandomFile
:: Delete the temp files
del "%tempFile%"
del "%tempFileTwo%"
endlocal
exit /b
:openRandomFile
set /a "randomNum=(%random% %% cnt) + 1"
(for /f "tokens=1* delims=:" %%A in (
'findstr "^%randomNum%:" "%tempFile%"'
) do (
echo Selection: %%B
start "" "%%B"
set /a "cnt -= 1"
rem /* Ensure to match the whole line, including the colon-separated count prefix,
rem    and escape `findstr` meta-characters to prevent wrong matches: */
set "LINE=%%A:%%B"
setlocal EnableDelayedExpansion
set "LINE=!LINE:=\!" & set "LINE=!LINE:.=.!"
set "LINE=!LINE:[=]!" & set "LINE=!LINE:]=]!"
set "LINE=!LINE:^=^!" & set "LINE=!LINE:$=$!"
findstr /V /X /I /C:"!LINE!" "!tempFile!" > "!tempFileTwo!"
endlocal
copy /y "%tempFileTwo%" "%tempFile%"
)) || (
rem /* Detect if `for /F` loop did not iterate, meaning that an already selected
rem    file would have become chosen once again, hence retry random selection: */
goto :openRandomFile
)
exit /b

然而,我会选择另一种策略来获得没有重复的文件,即建立一个文件列表,每个文件前面都有一个随机数,然后根据所述数字对该列表进行排序,最终从列表中提取前几个元素。

下面是一个可能的实现,使用一个临时文件来构建列表:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_TMPF=%TEMP%%~nx0_%RANDOM%.lst"
rem // Change into target directory:
pushd "%~1" && (
rem // Write to temporary file:
> "%_TMPF%" (
rem // Build list of matching files with a random number preceded:
for /F "delims=" %%F in ('dir /S /B /A:-D-H-S "*.mov" "*.mp4"') do (
set "FILE=%%F"
setlocal EnableDelayedExpansion
echo/!RANDOM!:!FILE!
endlocal
)
)
rem // Sort list of files by preceding random numbers:
sort "%_TMPF%" /O "%_TMPF%"
rem // Read from temporary file:
< "%_TMPF%" (
rem // Fetch the first three items from the list of files:
setlocal EnableDelayedExpansion
for /L %%I in (1,1,3) do (
set /P FILE="" && (
echo Selection: !FILE:*:=!
start "" "!FILE:*:=!"
)
)
endlocal
)
rem // Return from target directory:
popd
rem // Clean up temporary file:
del "%_TMPF%" 2> nul
)
endlocal
exit /B

这是另一种可能的方法,使用像$FILES[]这样的变量来构成伪数组:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Change into target directory:
pushd "%~1" && (
rem // Clean up (pseudo-)array variable:
for /F "delims==" %%V in ('set "$FILES[" 2^> nul') do set "%%V="
rem /* Build list of matching files in array with random number as indices
rem    plus a counter, just to avoid duplicate variable/element names: */
set /A "INDEX=0"
for /F "delims=" %%F in ('dir /S /B /A:-D-H-S "*.mov" "*.mp4"') do (
setlocal EnableDelayedExpansion & for %%R in (!RANDOM!_!INDEX!) do (
endlocal & set "$FILES[%%R]=%%F" & set /A "INDEX+=1"
)
)
rem /* Fetch the first three elements of the array of files, which becomes
rem    implicitly sorted by the indices by the `set` command: */
set /A "INDEX=0"
for /F "tokens=1* delims=:=" %%E in ('set "$FILES[" 2^> nul') do (
setlocal EnableDelayedExpansion
if !INDEX! lss 3 endlocal & (
echo Selection: %%F
start "" "%%F"
) else endlocal
set /A "INDEX+=1"
)
rem // Return from target directory:
popd
)
endlocal
exit /B

findstr /l /v /C:"%%B" "%tempFile%" > "%tempFileTwo%"

可能会恢复理智。

如果%%B包含空格,则"%%B"中的每个单独单词都将匹配。/c:指示findstr将带引号的字符串视为要匹配的字符串,而不是子字符串列表。

实现目标的一种更简单的方法是:

在主程序中,在pushd之前

:: remove variables starting $
FOR  /F "delims==" %%b In ('set $ 2^>Nul') DO SET "%%b="

其中$可以是您可能想要的任何字符串,如#opened。此行清除任何启动$的变量。

:openRandomFile中,设置randomNum后,

if defined $%randomNum% goto openRandomFile
set $%randomNum%=Y 

其建立标志CCD_ 13以防止该号码被处理不止一次。

我相信你可以计算出调整临时文件可以被删除——包括cnt的递减。。。

最新更新