Archivo por lotes para eliminar archivos anteriores a N días

Estoy buscando una manera de eliminar todos los archivos anteriores a 7 días en un archivo por lotes. He buscado en la web y he encontrado algunos ejemplos con cientos de líneas de código, y otros que requieren la instalación de utilidades de línea de comando adicionales para realizar la tarea.

Se pueden hacer cosas similares en BASH en solo un par de líneas de código. Parece que se podría hacer algo al menos remotamente fácil para los archivos por lotes en Windows. Estoy buscando una solución que funcione en un símbolo del sistema estándar de Windows, sin ningún tipo de utilidades adicionales. Por favor, tampoco PowerShell o Cygwin.

Disfrutar:

forfiles -p "C:\what\ever" -s -m *.* -d  -c "cmd /c del @path" 

Consulte la documentación del archivo para obtener más detalles.

Para obtener más golosinas, consulte el Índice AZ de la línea de comandos de Windows XP .

Si no tiene forfiles instalados en su máquina, cópielos desde cualquier Windows Server 2003 a su máquina con Windows XP en %WinDir%\system32\ . Esto es posible ya que el EXE es totalmente compatible entre Windows Server 2003 y Windows XP.

Las versiones posteriores de Windows y Windows Server lo tienen instalado por defecto.

Para Windows 7:

La syntax ha cambiado un poco. Por lo tanto, el comando actualizado es:

 forfiles /p "C:\what\ever" /s /m *.* /D - /C "cmd /c del @path" 

Ejecute los siguientes comandos :

 ROBOCOPY C:\source C:\destination /mov /minage:7 del C:\destination /q 

Mueva todos los archivos (usando / mov, que mueve los archivos y luego los elimina en lugar de / move que mueve los archivos completos que luego se eliminan) a través de robocopy a otra ubicación, y luego ejecute un comando de eliminación en esa ruta y usted es todo bueno.

Además, si tiene un directorio con muchos datos, puede usar /mir switch

Ok, me aburrí un poco y se me ocurrió esto, que contiene mi versión del reemplazo de época de Linux de un pobre limitado para el uso diario (sin retención de tiempo):

7daysclean.cmd

 @echo off setlocal ENABLEDELAYEDEXPANSION set day=86400 set /a year=day*365 set /a strip=day*7 set dSource=C:\temp call :epoch %date% set /a slice=epoch-strip for /f "delims=" %%f in ('dir /adhs /b /s %dSource%') do ( call :epoch %%~tf if !epoch! LEQ %slice% (echo DELETE %%f ^(%%~tf^)) ELSE echo keep %%f ^(%%~tf^) ) exit /b 0 rem Args[1]: Year-Month-Day :epoch setlocal ENABLEDELAYEDEXPANSION for /f "tokens=1,2,3 delims=-" %%d in ('echo %1') do set Years=%%d& set Months=%%e& set Days=%%f if "!Months:~0,1!"=="0" set Months=!Months:~1,1! if "!Days:~0,1!"=="0" set Days=!Days:~1,1! set /a Days=Days*day set /a _months=0 set i=1&& for %%m in (31 28 31 30 31 30 31 31 30 31 30 31) do if !i! LSS !Months! (set /a _months=!_months! + %%m*day&& set /a i+=1) set /a Months=!_months! set /a Years=(Years-1970)*year set /a Epoch=Years+Months+Days endlocal& set Epoch=%Epoch% exit /b 0 

USO

set /a strip=day*7 : Cambie 7 por el número de días que se mantendrá.

set dSource=C:\temp : este es el directorio de inicio para verificar los archivos.

NOTAS

Este es un código no destructivo, mostrará lo que hubiera pasado.

Cambio :

 if !epoch! LEQ %slice% (echo DELETE %%f ^(%%~tf^)) ELSE echo keep %%f ^(%%~tf^) 

a algo como:

 if !epoch! LEQ %slice% del /f %%f 

para que los archivos realmente se eliminen

Febrero : está codificado en 28 días. Los años bissextiles es un infierno para agregar, realmente. si alguien tiene una idea que no agregue 10 líneas de código, adelante y publíquelo, así que lo agrego a mi código.

época : no tomé el tiempo en consideración, ya que la necesidad es eliminar archivos anteriores a una determinada fecha, tomar horas / minutos habría eliminado los archivos de un día que estaba destinado a mantener.

LIMITACIÓN

época toma por sentado que su formato de fecha corta es AAAA-MM-DD. Necesitaría ser adaptado para otras configuraciones o una evaluación en tiempo de ejecución (leer sShortTime, configuración ligada al usuario, configurar el orden de campo apropiado en un filtro y usar el filtro para extraer los datos correctos del argumento).

¿Mencioné que odio el auto-formateo de este editor? elimina las líneas en blanco y el copiar y pegar es un infierno.

Espero que esto ayude.

 forfiles /p "v:" /s /m *.* /d -3 /c "cmd /c del @path" 

Deberías hacer /d -3 (3 días antes) Esto funciona bien para mí. Entonces todos los lotes complicados podrían estar en el basurero. Además, los forfiles no admiten rutas de acceso UNC, por lo tanto, realice una conexión de red a un disco específico.

Eche un vistazo a mi respuesta a una pregunta similar :

 REM del_old.bat REM usage: del_old MM-DD-YYY for /f "tokens=*" %%a IN ('xcopy *.* /d:%1 /L /I null') do if exist %%~nxa echo %%~nxa >> FILES_TO_KEEP.TXT for /f "tokens=*" %%a IN ('xcopy *.* /L /I /EXCLUDE:FILES_TO_KEEP.TXT null') do if exist "%%~nxa" del "%%~nxa" 

Esto elimina archivos anteriores a una fecha determinada. Estoy seguro de que se puede modificar para volver siete días después de la fecha actual.

actualización: Noté que HerbCSO ha mejorado en el script anterior. Recomiendo usar su versión en su lugar.

Mi comando es

 forfiles -p "d:\logs" -s -m*.log -d-15 -c"cmd /c del @PATH\@FILE" 

@PATH – es solo una ruta en mi caso, así que tuve que usar @PATH\@FILE

también forfiles /? no funciona para mí también, pero forfiles (sin “?”) funcionó bien.

Y la única pregunta que tengo: cómo agregar varias máscaras (por ejemplo, ” .log | .bak”)?

Todo esto con respecto a forfiles.exe que descargué aquí (en Windows XP)

Pero si está usando el servidor de Windows, forfiles.exe ya debería estar allí y difiere de la versión de ftp. Es por eso que debería modificar el comando.

Para Windows Server 2003 estoy usando este comando:

 forfiles -p "d:\Backup" -s -m *.log -d -15 -c "cmd /c del @PATH" 

Para Windows Server 2008 R2:

 forfiles /P c:\sql_backups\ /S /M *.sql /D -90 /C "cmd /c del @PATH" 

Esto eliminará todos los archivos .sql anteriores a 90 días.

Copie este código y guárdelo como DelOldFiles.vbs.

USO SÓLO EN COMANDO PRONTO: cscript DelOldFiles.vbs 15

15 significa eliminar archivos anteriores a 15 días.

  'copy from here Function DeleteOlderFiles(whichfolder) Dim fso, f, f1, fc, n, ThresholdDate Set fso = CreateObject("Scripting.FileSystemObject") Set f = fso.GetFolder(whichfolder) Set fc = f.Files Set objArgs = WScript.Arguments n = 0 If objArgs.Count=0 Then howmuchdaysinpast = 0 Else howmuchdaysinpast = -objArgs(0) End If ThresholdDate = DateAdd("d", howmuchdaysinpast, Date) For Each f1 in fc If f1.DateLastModified 

Use para archivos.

Hay diferentes versiones. Los primeros usan parámetros de estilo de Unix.

Mi versión (para el servidor 2000 – note que no hay espacio después de los interruptores) –

 forfiles -p"C:\what\ever" -s -m*.* -d -c"cmd /c del @path" 

Para agregar archivos a XP, obtenga el exe de ftp://ftp.microsoft.com/ResKit/y2kfix/x86/

y agregarlo a C: \ WINDOWS \ system32

IMO, JavaScript se está convirtiendo gradualmente en un estándar de scripting universal: probablemente esté disponible en más productos que cualquier otro lenguaje de scripting (en Windows, está disponible con el Windows Scripting Host). Tengo que limpiar viejos archivos en muchas carpetas, así que aquí hay una función de JavaScript para hacer eso:

 // run from an administrator command prompt (or from task scheduler with full rights): wscript jscript.js // debug with: wscript /d /x jscript.js var fs = WScript.CreateObject("Scripting.FileSystemObject"); clearFolder('C:\\temp\\cleanup'); function clearFolder(folderPath) { // calculate date 3 days ago var dateNow = new Date(); var dateTest = new Date(); dateTest.setDate(dateNow.getDate() - 3); var folder = fs.GetFolder(folderPath); var files = folder.Files; for( var it = new Enumerator(files); !it.atEnd(); it.moveNext() ) { var file = it.item(); if( file.DateLastModified < dateTest) { var filename = file.name; var ext = filename.split('.').pop().toLowerCase(); if (ext != 'exe' && ext != 'dll') { file.Delete(true); } } } var subfolders = new Enumerator(folder.SubFolders); for (; !subfolders.atEnd(); subfolders.moveNext()) { clearFolder(subfolders.item().Path); } } 

Para que se borre cada carpeta, simplemente agregue otra llamada a la función clearFolder (). Este código particular también conserva los archivos exe y dll, y también limpia las subcarpetas.

¿Qué tal esta modificación en 7daysclean.cmd para tener en cuenta un año bisiesto?

¡Se puede hacer en menos de 10 líneas de encoding!

 set /a Leap=0 if (Month GEQ 2 and ((Years%4 EQL 0 and Years%100 NEQ 0) or Years%400 EQL 0)) set /a Leap=day set /a Months=!_months!+Leap 

Editar por Mofi:

La condición anterior aportada por JR se evalúa siempre como falsa debido a una syntax no válida.

Y el Month GEQ 2 también es incorrecto porque agregar 86400 segundos por un día más se debe hacer en un año bisiesto solo para los meses de marzo a diciembre, pero no para febrero.

Un código que funcione para tener en cuenta el día bisiesto, solo en el año actual, en el archivo por lotes 7daysclean.cmd publicado por Jay sería:

 set "LeapDaySecs=0" if %Month% LEQ 2 goto CalcMonths set /a "LeapRule=Years%%4" if %LeapRule% NEQ 0 goto CalcMonths rem The other 2 rules can be ignored up to year 2100. set /A "LeapDaySecs=day" :CalcMonths set /a Months=!_months!+LeapDaySecs 

Para Windows 2012 R2, lo siguiente funcionaría:

  forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c del @path" 

para ver los archivos que se eliminarán usa esto

  forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c echo @path @fdate" 

Con frecuencia, hay preguntas relacionadas con la fecha / hora para resolver con el archivo por lotes. Pero el intérprete de línea de comandos cmd.exe no tiene función para los cálculos de fecha / hora. Ya se han publicado muchas buenas soluciones de trabajo que usan aplicaciones de consola adicionales o scripts, en otras páginas de Stack Overflow y en otros sitios web.

Común para las operaciones basadas en fecha / hora es el requisito para convertir una cadena de fecha / hora a segundos desde un día determinado. Muy común es 1970-01-01 00:00:00 UTC. Pero cualquier día posterior también podría usarse dependiendo del rango de fechas requerido para respaldar una tarea específica.

Jay publicó 7daysclean.cmd que contiene una solución rápida de “fecha a segundos” para el intérprete de línea de comandos cmd.exe . Pero no toma en cuenta los años bisiestos. JR publicó un complemento para tomar en cuenta el día bisiesto en el año actual, pero ignorando los otros años bisiestos desde el año base, es decir, desde 1970.

Utilizo desde hace 20 años tablas estáticas (matrices) creadas una vez con una pequeña función C para obtener rápidamente el número de días, incluidos los días bisiestos desde 1970-01-01 en funciones de conversión de fecha / hora en mis aplicaciones escritas en C / C ++.

Este método de tabla muy rápido se puede usar también en código de lote usando el comando FOR . Así que decidí codificar la subrutina de lote GetSeconds que calcula el número de segundos desde 1970-01-01 00:00:00 UTC para una cadena de fecha / hora pasada a esta rutina.

Nota: Los segundos intercalares no se tienen en cuenta ya que los sistemas de archivos de Windows tampoco son compatibles con los segundos intercalares.

Primero, las tablas:

  1. Días desde 1970-01-01 00:00:00 UTC para cada año, incluidos los días bisiestos.

     1970 - 1979: 0 365 730 1096 1461 1826 2191 2557 2922 3287 1980 - 1989: 3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 1990 - 1999: 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 2000 - 2009: 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245 2010 - 2019: 14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 2020 - 2029: 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 2030 - 2039: 21915 22280 22645 23011 23376 23741 24106 24472 24837 25202 2040 - 2049: 25567 25933 26298 26663 27028 27394 27759 28124 28489 28855 2050 - 2059: 29220 29585 29950 30316 30681 31046 31411 31777 32142 32507 2060 - 2069: 32872 33238 33603 33968 34333 34699 35064 35429 35794 36160 2070 - 2079: 36525 36890 37255 37621 37986 38351 38716 39082 39447 39812 2080 - 2089: 40177 40543 40908 41273 41638 42004 42369 42734 43099 43465 2090 - 2099: 43830 44195 44560 44926 45291 45656 46021 46387 46752 47117 2100 - 2106: 47482 47847 48212 48577 48942 49308 49673 

    El cálculo de los segundos para el año 2039 a 2106 con época que comienza en 1970-01-01 solo es posible con el uso de una variable de 32 bits sin signo, es decir, sin signo largo (o sin signo) en C / C ++.

    Pero el uso de cmd.exe para las expresiones matemáticas es una variable de 32 bits con signo. Por lo tanto, el valor máximo es 2147483647 (0x7FFFFFFF) que es 2038-01-19 03:14:07.

  2. Información del año bisiesto (No / Sí) para los años 1970 a 2106.

     1970 - 1989: NNYNNNYNNNYNNNYNNNYN 1990 - 2009: NNYNNNYNNNYNNNYNNNYN 2010 - 2029: NNYNNNYNNNYNNNYNNNYN 2030 - 2049: NNYNNNYNNNYNNNYNNNYN 2050 - 2069: NNYNNNYNNNYNNNYNNNYN 2070 - 2089: NNYNNNYNNNYNNNYNNNYN 2090 - 2106: NNYNNNYNNNNNNNYNN ^ year 2100 
  3. Número de días hasta el primer día de cada mes en el año actual.

      Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec Year with 365 days: 0 31 59 90 120 151 181 212 243 273 304 334 Year with 366 days: 0 31 60 91 121 152 182 213 244 274 305 335 

Convertir una fecha en una cantidad de segundos desde 1970-01-01 es bastante fácil usando esas tablas.

¡Atención por favor!

El formato de las cadenas de fecha y hora depende de la región de Windows y de la configuración de idioma. Los delimitadores y el orden de los tokens asignados a las variables de entorno Day , Month y Year en el primer bucle FOR de GetSeconds deben adaptarse al formato de fecha / hora local si es necesario.

Es necesario adaptar la cadena de fecha de la variable de entorno si el formato de fecha en la variable de entorno DATE es diferente al formato de fecha utilizado por el comando FOR en %%~tF .

Por ejemplo, cuando %DATE% expande a Sun 02/08/2015 mientras %%~tF expande a 02/08/2015 07:38 PM el código siguiente se puede usar con la modificación de la línea 4 para:

 call :GetSeconds "%DATE:~4% %TIME%" 

Esto da como resultado el paso a la subrutina el 02/08/2015 – la cadena de fecha sin las 3 letras de la abreviatura del día de la semana y el carácter del espacio de separación.

Alternativamente, se podría utilizar para pasar la fecha actual en el formato correcto:

 call :GetSeconds "%DATE:~-10% %TIME%" 

Ahora los últimos 10 caracteres de la cadena de fecha se pasan a la función GetSeconds y por lo tanto no importa si la cadena de fecha de la variable de entorno DATE es con o sin día de la semana siempre y cuando el día y el mes estén siempre con 2 dígitos en el orden esperado, es decir, en formato dd/mm/yyyy o dd.mm.yyyy

Aquí está el código del lote con comentarios explicativos que solo muestra qué archivo eliminar y qué archivo guardar en el árbol de carpetas C:\Temp , ver el código del primer ciclo FOR .

 @echo off setlocal EnableExtensions EnableDelayedExpansion rem Get seconds since 1970-01-01 for current date and time. call :GetSeconds "%DATE% %TIME%" rem Subtract seconds for 7 days from seconds value. set /A "LastWeek=Seconds-7*86400" rem For each file in each subdirectory of C:\Temp get last modification date rem (without seconds -> append second 0) and determine the number of seconds rem since 1970-01-01 for this date/time. The file can be deleted if seconds rem value is lower than the value calculated above. for /F "delims=" %%F in ('dir /ADHS /B /S "C:\Temp"') do ( call :GetSeconds "%%~tF:0" rem if !Seconds! LSS %LastWeek% del /F "%%~fF" if !Seconds! LEQ %LastWeek% ( echo Delete "%%~fF" ) else ( echo Keep "%%~fF" ) ) endlocal goto :EOF rem No validation is made for best performance. So make sure that date rem and hour in string is in a format supported by the code below like rem MM/DD/YYYY hh:mm:ss or M/D/YYYY h:m:s for English US date/time. :GetSeconds rem If there is " AM" or " PM" in time string because of using 12 hour rem time format, remove those 2 strings and in case of " PM" remember rem that 12 hours must be added to the hour depending on hour value. set "DateTime=%~1" set "Add12Hours=0" if "%DateTime: AM=%" NEQ "%DateTime%" ( set "DateTime=%DateTime: AM=%" ) else if "%DateTime: PM=%" NEQ "%DateTime%" ( set "DateTime=%DateTime: PM=%" set "Add12Hours=1" ) rem Get year, month, day, hour, minute and second from first parameter. for /F "tokens=1-6 delims=,-./: " %%A in ("%DateTime%") do ( rem For English US date MM/DD/YYYY or M/D/YYYY set "Day=%%B" & set "Month=%%A" & set "Year=%%C" rem For German date DD.MM.YYYY or English UK date DD/MM/YYYY rem set "Day=%%A" & set "Month=%%B" & set "Year=%%C" set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F" ) rem echo Date/time is: %Year%-%Month%-%Day% %Hour%:%Minute%:%Second% rem Remove leading zeros from the date/time values or calculation could be wrong. if "%Month:~0,1%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" ) if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" ) if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" ) if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" ) if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" ) rem Add 12 hours for time range 01:00:00 PM to 11:59:59 PM, rem but keep the hour as is for 12:00:00 PM to 12:59:59 PM. if "%Add12Hours%" == "1" ( if %Hour% LSS 12 set /A Hour+=12 ) set "DateTime=" set "Add12Hours=" rem Must use 2 arrays as more than 31 tokens are not supported rem by command line interpreter cmd.exe respectively command FOR. set /A "Index1=Year-1979" set /A "Index2=Index1-30" if %Index1% LEQ 30 ( rem Get number of days to year for the years 1980 to 2009. for /F "tokens=%Index1% delims= " %%Y in ("3652 4018 4383 4748 5113 5479 5844 6209 6574 6940 7305 7670 8035 8401 8766 9131 9496 9862 10227 10592 10957 11323 11688 12053 12418 12784 13149 13514 13879 14245") do set "Days=%%Y" for /F "tokens=%Index1% delims= " %%L in ("YNNNYNNNYNNNYNNNYNNNY NNNYNNNYN") do set "LeapYear=%%L" ) else ( rem Get number of days to year for the years 2010 to 2038. for /F "tokens=%Index2% delims= " %%Y in ("14610 14975 15340 15706 16071 16436 16801 17167 17532 17897 18262 18628 18993 19358 19723 20089 20454 20819 21184 21550 21915 22280 22645 23011 23376 23741 24106 24472 24837") do set "Days=%%Y" for /F "tokens=%Index2% delims= " %%L in ("NNYNNNYNNNYNNNYNNNYNN NYNNNYNN") do set "LeapYear=%%L" ) rem Add the days to month in year. if "%LeapYear%" == "N" ( for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M" ) else ( for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M" ) rem Add the complete days in month of year. set /A "Days+=Day-1" rem Calculate the seconds which is easy now. set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second" rem Exit this subroutine goto :EOF 

Para un rendimiento óptimo, sería mejor eliminar todos los comentarios, es decir, todas las líneas que comiencen con rem después de 0-4 espacios iniciales.

Y las matrices también pueden hacerse más pequeñas, es decir, disminuyendo el intervalo de tiempo de 1980-01-01 00:00:00 a 2038-01-19 03:14:07 como actualmente es compatible con el código de lote anterior, por ejemplo, para 2015-01 -01 a 2019-12-31 como utiliza el siguiente código, que realmente elimina archivos de más de 7 días en el árbol de carpetas C:\Temp .

Además, el código de lote a continuación está optimizado para el formato de 24 horas.

 @echo off setlocal EnableDelayedExpansion call :GetSeconds "%DATE:~-10% %TIME%" set /A "LastWeek=Seconds-7*86400" for /F "delims=" %%F in ('dir /ADHS /B /S "C:\Temp"') do ( call :GetSeconds "%%~tF:0" if !Seconds! LSS %LastWeek% del /F "%%~fF" ) endlocal goto :EOF :GetSeconds for /F "tokens=1-6 delims=,-./: " %%A in ("%~1") do ( set "Day=%%B" & set "Month=%%A" & set "Year=%%C" set "Hour=%%D" & set "Minute=%%E" & set "Second=%%F" ) if "%Month:~0,1%" EQU "0" ( if "%Month:~1%" NEQ "" set "Month=%Month:~1%" ) if "%Day:~0,1%" EQU "0" ( if "%Day:~1%" NEQ "" set "Day=%Day:~1%" ) if "%Hour:~0,1%" EQU "0" ( if "%Hour:~1%" NEQ "" set "Hour=%Hour:~1%" ) if "%Minute:~0,1%" EQU "0" ( if "%Minute:~1%" NEQ "" set "Minute=%Minute:~1%" ) if "%Second:~0,1%" EQU "0" ( if "%Second:~1%" NEQ "" set "Second=%Second:~1%" ) set /A "Index=Year-2014" for /F "tokens=%Index% delims= " %%Y in ("16436 16801 17167 17532 17897") do set "Days=%%Y" for /F "tokens=%Index% delims= " %%L in ("NYNNN") do set "LeapYear=%%L" if "%LeapYear%" == "N" ( for /F "tokens=%Month% delims= " %%M in ("0 31 59 90 120 151 181 212 243 273 304 334") do set /A "Days+=%%M" ) else ( for /F "tokens=%Month% delims= " %%M in ("0 31 60 91 121 152 182 213 244 274 305 335") do set /A "Days+=%%M" ) set /A "Days+=Day-1" set /A "Seconds=Days*86400+Hour*3600+Minute*60+Second" goto :EOF 

Para obtener aún más información sobre los formatos de fecha y hora y las comparaciones de tiempo de archivos en Windows, consulte mi respuesta en Descubra si el archivo es anterior a 4 horas en un archivo por lotes con mucha información adicional sobre las horas de archivo.

¿Puedo agregar una contribución humilde a este hilo que ya es valioso? Estoy descubriendo que otras soluciones podrían eliminar el texto de error real, pero están ignorando el% ERRORLEVEL% que señala un error en mi aplicación. Y legítimamente quiero% ERRORLEVEL% siempre que no sea el error “No se encontraron archivos”.

Algunos ejemplos:

Depuración y eliminación del error específicamente:

 forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 | findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&ECHO found error||echo found success 

Usar un delineador para devolver el éxito o el fracaso ERRORLEVEL:

 forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 | findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&EXIT /B 1||EXIT /B 0 

Usar un oneliner para mantener el ERRORLEVEL en cero para tener éxito dentro del contexto de un archivo por lotes en medio de otro código (ver> nul restablece el ERRORLEVEL):

 forfiles /p "[file path...]\IDOC_ARCHIVE" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 | findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&ECHO found error||ver > nul 

Para un paso de trabajo CmdExec del Agente SQL Server, ingresé en lo siguiente. No sé si es un error, pero el CmdExec dentro del paso solo reconoce la primera línea de código:

 cmd /e:on /c "forfiles /p "C:\SQLADMIN\MAINTREPORTS\SQL2" /s /m *.txt /d -1 /c "cmd /c del @path" 2>&1 | findstr /V /O /C:"ERROR: No files found with the specified search criteria."2>&1 | findstr ERROR&&EXIT 1||EXIT 0"&exit %errorlevel% 

Si tiene el kit de recursos de XP, puede usar robocopy para mover todos los directorios antiguos a un solo directorio, luego use rmdir para eliminar solo ese:

 mkdir c:\temp\OldDirectoriesGoHere robocopy c:\logs\SoManyDirectoriesToDelete\ c:\temp\OldDirectoriesGoHere\ /move /minage:7 rmdir /s /qc:\temp\OldDirectoriesGoHere 

Creo que la respuesta de e.James es buena, ya que funciona con versiones no modificadas de Windows desde Windows 2000 SP4 (y posiblemente antes), pero requería escribir en un archivo externo. Aquí hay una versión modificada que no crea un archivo de texto externo manteniendo la compatibilidad:

 REM del_old.cmd REM usage: del_old MM-DD-YYYY setlocal enabledelayedexpansion for /f "tokens=*" %%a IN ('xcopy *.* /d:%1 /L /I null') do @if exist "%%~nxa" set "excludefiles=!excludefiles!;;%%~nxa;;" for /f "tokens=*" %%a IN ('dir /b') do @(@echo "%excludefiles%"|FINDSTR /C:";;%%a;;">nul || if exist "%%~nxa" DEL /F /Q "%%a">nul 2>&1) 

Para ser fiel a la pregunta original, aquí está en una secuencia de comandos que hace TODA la matemática por usted si la llama con el número de días como parámetro:

 REM del_old_compute.cmd REM usage: del_old_compute N setlocal enabledelayedexpansion set /a days=%1&set cur_y=%DATE:~10,4%&set cur_m=%DATE:~4,2%&set cur_d=%DATE:~7,2% for /f "tokens=1 delims==" %%a in ('set cur_') do if "!%%a:~0,1!"=="0" set /a %%a=!%%a:~1,1!+0 set mo_2=28&set /a leapyear=cur_y*10/4 if %leapyear:~-1% equ 0 set mo_2=29 set mo_1=31&set mo_3=31&set mo_4=30&set mo_5=31 set mo_6=30&set mo_7=31&set mo_8=31&set mo_9=30 set mo_10=31&set mo_11=30&set mo_12=31 set /a past_y=(days/365) set /a monthdays=days-((past_y*365)+((past_y/4)*1))&&set /a past_y=cur_y-past_y&set months=0 :setmonth set /a minusmonth=(cur_m-1)-months if %minusmonth% leq 0 set /a minusmonth+=12 set /a checkdays=(mo_%minusmonth%) if %monthdays% geq %checkdays% set /a months+=1&set /a monthdays-=checkdays&goto :setmonth set /a past_m=cur_m-months set /a lastmonth=cur_m-1 if %lastmonth% leq 0 set /a lastmonth+=12 set /a lastmonth=mo_%lastmonth% set /a past_d=cur_d-monthdays&set adddays=:: if %past_d% leq 0 (set /a past_m-=1&set adddays=) if %past_m% leq 0 (set /a past_m+=12&set /a past_y-=1) set mo_2=28&set /a leapyear=past_y*10/4 if %leapyear:~-1% equ 0 set mo_2=29 %adddays%set /a past_d+=mo_%past_m% set d=%past_m%-%past_d%-%past_y% for /f "tokens=*" %%a IN ('xcopy *.* /d:%d% /L /I null') do @if exist "%%~nxa" set "excludefiles=!excludefiles!;;%%~nxa;;" for /f "tokens=*" %%a IN ('dir /b') do @(@echo "%excludefiles%"|FINDSTR /C:";;%%a;;">nul || if exist "%%~nxa" DEL /F /Q "%%a">nul 2>&1) 

NOTA: El código anterior tiene en cuenta los años bisiestos, así como el número exacto de días en cada mes. El único máximo es el número total de días que ha habido desde 0/0/0 (después de eso devuelve años negativos).

NOTA: La matemática solo tiene una dirección; no puede obtener correctamente fechas futuras de entrada negativa (lo intentará, pero probablemente pasará el último día del mes).

Es posible que puedas lograr esto. Puede echar un vistazo a esta pregunta , para un ejemplo más simple. La complejidad viene cuando comienzas a comparar las fechas. Puede ser fácil saber si la fecha es mayor o no, pero hay muchas situaciones a considerar si necesita obtener la diferencia entre dos fechas.

En otras palabras, no intente inventar esto, a menos que realmente no pueda usar las herramientas de terceros.

esto no es nada sorprendente, pero necesitaba hacer algo como esto hoy y ejecutarlo como tarea progtwigda, etc.

archivo por lotes, DelFilesOlderThanNDays.bat a continuación con exec de muestra con params:

DelFilesOlderThanNDays.bat 7 C: \ dir1 \ dir2 \ dir3 \ logs * .log

 echo off cls Echo( SET keepDD=%1 SET logPath=%2 :: example C:\dir1\dir2\dir3\logs SET logFileExt=%3 SET check=0 IF [%3] EQU [] SET logFileExt=*.log & echo: file extention not specified (default set to "*.log") IF [%2] EQU [] echo: file directory no specified (a required parameter), exiting! & EXIT /B IF [%1] EQU [] echo: number of days not specified? :) echo( echo: in path [ %logPath% ] echo: finding all files like [ %logFileExt% ] echo: older than [ %keepDD% ] days echo( :: :: :: LOG echo: >> c:\trimLogFiles\logBat\log.txt echo: executed on %DATE% %TIME% >> c:\trimLogFiles\logBat\log.txt echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt echo: in path [ %logPath% ] >> c:\trimLogFiles\logBat\log.txt echo: finding all files like [ %logFileExt% ] >> c:\trimLogFiles\logBat\log.txt echo: older than [ %keepDD% ] days >> c:\trimLogFiles\logBat\log.txt echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt :: FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c echo @path" >> c:\trimLogFiles\logBat\log.txt 2<&1 IF %ERRORLEVEL% EQU 0 ( FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c echo @path" ) :: :: :: LOG IF %ERRORLEVEL% EQU 0 ( echo: >> c:\trimLogFiles\logBat\log.txt echo: deleting files ... >> c:\trimLogFiles\logBat\log.txt echo: >> c:\trimLogFiles\logBat\log.txt SET check=1 ) :: :: IF %check% EQU 1 ( FORFILES /p %logPath% /s /m %logFileExt% /d -%keepDD% /c "cmd /c del @path" ) :: :: RETURN & LOG :: IF %ERRORLEVEL% EQU 0 echo: deletion successfull! & echo: deletion successfull! >> c:\trimLogFiles\logBat\log.txt echo: ---------------------------------------------------------- >> c:\trimLogFiles\logBat\log.txt 

Ampliando la respuesta de aku, veo a mucha gente preguntando sobre los caminos UNC. Simplemente mapear la ruta de acceso de unc a una letra de unidad hará feliz a los archivos for. Mapping and unmapping of drives can be done programmatically in a batch file, for example.

 net use Z: /delete net use Z: \\unc\path\to\my\folder forfiles /p Z: /s /m *.gz /D -7 /C "cmd /c del @path" 

This will delete all files with a .gz extension that are older than 7 days. If you want to make sure Z: isn’t mapped to anything else before using it you could do something simple as

 net use Z: \\unc\path\to\my\folder if %errorlevel% equ 0 ( forfiles /p Z: /s /m *.gz /D -7 /C "cmd /c del @path" ) else ( echo "Z: is already in use, please use another drive letter!" ) 

ROBOCOPY works great for me. Originally suggested my Iman. But instead of moving the files/folders to a temporary directory then deleting the contents of the temporary folder, move the files to the trash!!!

This is is a few lines of my backup batch file for example:

 SET FilesToClean1=C:\Users\pauls12\Temp SET FilesToClean2=C:\Users\pauls12\Desktop\1616 - Champlain\Engineering\CAD\Backups SET RecycleBin=C:\$Recycle.Bin\S-1-5-21-1480896384-1411656790-2242726676-748474 robocopy "%FilesToClean1%" "%RecycleBin%" /mov /MINLAD:15 /XA:SH /NC /NDL /NJH /NS /NP /NJS robocopy "%FilesToClean2%" "%RecycleBin%" /mov /MINLAD:30 /XA:SH /NC /NDL /NJH /NS /NP /NJS 

It cleans anything older than 15 days out of my ‘Temp’ folder and 30 days for anything in my AutoCAD backup folder. I use variables because the line can get quite long and I can reuse them for other locations. You just need to find the dos path to your recycle bin associated with your login.

This is on a work computer for me and it works. I understand that some of you may have more restrictive rights but give it a try anyway;) Search Google for explanations on the ROBOCOPY parameters.

¡Aclamaciones!

Gosh, a lot of answers already. A simple and convenient route I found was to execute ROBOCOP.EXE twice in sequential order from a single Windows command line instruction using the & parameter.

 ROBOCOPY.EXE SOURCE-DIR TARGET-DIR *.* /MOV /MINAGE:30 & ROBOCOPY.EXE SOURCE-DIR TARGET-DIR *.* /MOV /MINAGE:30 /PURGE 

In this example it works by picking all files ( . ) that are older than 30 days old and moving them to the target folder. The second command does the same again with the addition of the PURGE command which means remove files in the target folder that don’t exist in the source folder. So essentially, the first command MOVES files and the second DELETES because they no longer exist in the source folder when the second command is invoked.

Consult ROBOCOPY’s documentation and use the /L switch when testing.

You’re probably not going to find a totally non install solution short of a complicated bat file or windows script. I use delen , drop it in a system directory then use it like the regular del command in any bat file.

This one did it for me. It works with a date and you can substract the wanted amount in years to go back in time:

 @echo off set m=%date:~-7,2% set /A m set dateYear=%date:~-4,4% set /A dateYear -= 2 set DATE_DIR=%date:~-10,2%.%m%.%dateYear% forfiles /p "C:\your\path\here\" /s /m *.* /d -%DATE_DIR% /c "cmd /c del @path /F" pause 

the /F in the cmd /c del @path /F forces the specific file to be deleted in some the cases the file can be read-only.

the dateYear is the year Variable and there you can change the substract to your own needs

My script to delete files older than a specific year :

 @REM _______ GENERATE A CMD TO DELETE FILES OLDER THAN A GIVEN YEAR @REM _______ (given in _olderthanyear variable) @REM _______ (you must LOCALIZE the script depending on the dir cmd console output) @REM _______ (we assume here the following line's format "11/06/2017 15:04 58 389 SpeechToText.zip") @set _targetdir=c:\temp @set _olderthanyear=2017 @set _outfile1="%temp%\deleteoldfiles.1.tmp.txt" @set _outfile2="%temp%\deleteoldfiles.2.tmp.txt" @if not exist "%_targetdir%" (call :process_error 1 DIR_NOT_FOUND "%_targetdir%") & (goto :end) :main @dir /adhs /s /b %_targetdir%\*>%_outfile1% @for /F "tokens=*" %%F in ('type %_outfile1%') do @call :process_file_path "%%F" %_outfile2% @goto :end :end @rem ___ cleanup and exit @if exist %_outfile1% del %_outfile1% @if exist %_outfile2% del %_outfile2% @goto :eof :process_file_path %1 %2 @rem ___ get date info of the %1 file path @dir %1 | find "/" | find ":" > %2 @for /F "tokens=*" %%L in ('type %2') do @call :process_line "%%L" %1 @goto :eof :process_line %1 %2 @rem ___ generate a del command for each file older than %_olderthanyear% @set _var=%1 @rem LOCALIZE HERE (char-offset,string-length) @set _fileyear=%_var:~0,4% @set _fileyear=%_var:~7,4% @set _filepath=%2 @if %_fileyear% LSS %_olderthanyear% echo @REM %_fileyear% @if %_fileyear% LSS %_olderthanyear% echo @del %_filepath% @goto :eof :process_error %1 %2 @echo RC=%1 MSG=%2 %3 @goto :eof