Arquivo em lote para excluir arquivos com mais de N dias

677

Estou procurando uma maneira de excluir todos os arquivos com mais de 7 dias em um arquivo em lotes. Pesquisei na web e encontrei alguns exemplos com centenas de linhas de código e outros que exigiam a instalação de utilitários extras de linha de comando para realizar a tarefa.

Coisas semelhantes podem ser feitas no BASH em apenas algumas linhas de código. Parece que algo pelo menos remotamente fácil poderia ser feito para arquivos em lote no Windows. Estou procurando uma solução que funcione em um prompt de comando padrão do Windows, sem utilitários extras. Por favor, não PowerShell ou Cygwin também.

Kibbee
fonte
8
Jeff Atwood respondeu isso no Serverfault, que eu acho que deveria ser documentado aqui. serverfault.com/questions/49614/delete-files-older-than-x-days
Dusty
Um novo método baseado em um arquivo .BAT que usa comandos internos do CMD.EXE foi publicado aqui: stackoverflow.com/questions/9746778/…
Aacini
1
gehrcke.de/timegaps foi projetado para esse fim. Ele ainda permite um esquema de exclusão mais sofisticado: além de manter os arquivos dos últimos 7 dias, por exemplo, também permite manter um arquivo para cada uma das últimas 8 semanas, 12, meses, 2 anos.
Dr. Jan-Philip Gehrcke

Respostas:

1071

Aproveitar:

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

Veja a forfilesdocumentação para mais detalhes.

Para mais guloseimas, consulte Um índice AZ da linha de comando do Windows XP .

Se você não tiver forfilesinstalado em sua máquina, copie-o de qualquer Windows Server 2003 para sua máquina Windows XP em %WinDir%\system32\. Isso é possível, pois o EXE é totalmente compatível entre o Windows Server 2003 e o Windows XP.

Versões posteriores do Windows e Windows Server o instalam por padrão.

Para Windows 7 e mais recente (incluindo Windows 10):

A sintaxe mudou um pouco. Portanto, o comando atualizado é:

forfiles /p "C:\what\ever" /s /m *.* /D -<number of days> /C "cmd /c del @path"
aku
fonte
12
Deve ser del @FILE, caso importa Também eu sugiro usar / c echo @FILE para testar
Russell Steen
40
@ Russell: @ PATH é o caminho completo, incluindo o nome. @FILE é apenas o nome; portanto, se você estiver lidando com subpastas, ele não funcionará.
gregmac
86
Observe que se você quiser arquivos com MAIS DE 10 dias, precisará especificar -d "-10". -ve significa "mais antigo que", + ve significa "mais recente que". Você também pode especificar o formato DDMMYY ou -DDMMYY como o parâmetro para -d.
gregmac
42
Eu usei esta sintaxe no Win Server 2008: forfiles / P "C: \ Mysql_backup" / S / M * .sql / D -30 / C "CMD / c del @PATH"
jman
9
Esta resposta funciona quando "mais de 7 dias" é definido como "modificado há mais de 7 dias" em vez de "criado há mais de 7 dias". Como o último pode ser alcançado?
TimS 18/10/2013
76

Execute os seguintes comandos :

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

Mova todos os arquivos (usando / mov, que move arquivos e os exclui, em oposição a / move, que move arquivos de arquivos inteiros que são excluídos) via robocopy para outro local e, em seguida, execute um comando de exclusão nesse caminho e pronto. Boa.

Além disso, se você tiver um diretório com muitos dados, poderá usar o /mirswitch

Iman
fonte
5
Para a maior parte, esta é uma resposta bastante impraticável. Se eu tiver um diretório com muitos dados, não haverá um bom funcionamento. Eu iria com um dos a resposta que faz isso "in place"
adamb0mb
5
@ adamb0mb de maneira alguma é impraticável - se "destination" estiver no mesmo sistema de arquivos que "source" , a operação de movimentação será bem leve. Como a robocópia é realmente robusta, ela funciona de fato para qualquer tamanho de diretório, nomes de arquivos obscuros, comprimentos de caminho basicamente arbitrários e inclui diretórios, se você precisar - algo que certamente não pode ser dito sobre muitos outros utilitários do Windows. Eu usaria rd /s /q c:\destinationo delcomando em vez do comando ou mesmo outra robocopy /mir c:\emptydir c:\destinationexecução para esvaziar o diretório se você espera problemas com os nomes dos arquivos.
Syneticon-dj
12
Além disso, a robocopy suporta caminhos UNC - o que, forfilesda resposta aceita, não serve .
Syneticon-dj
3
Meus pensamentos estavam mais próximos: "Meus arquivos já estão onde eu os quero. Não quero ter que movê-los". "Excluir arquivos é logicamente o que você está fazendo, então faça isso. Não sobrecarregue o Robocopy para fazer isso"
adamb0mb
3
Eu recomendo este one-liner para exibir todos os arquivos em C:\testmais de 7 dias. Para excluir os arquivos, altere echopara del:forfiles /p "C:\test" /m "*.*" /c "cmd /c echo @file" /D -7
PowerUser
25

Ok ficou um pouco entediado e surgiu com isso, que contém a minha versão da substituição de época do pobre homem Linux limitada para uso diário (sem retenção de tempo):

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 /a-d-h-s /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: Altere 7 para o número de dias a serem mantidos.

set dSource=C:\temp : Este é o diretório inicial para procurar arquivos.

NOTAS

Este é um código não destrutivo, exibirá o que teria acontecido.

Mudança :

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

para algo como:

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

para que os arquivos sejam realmente excluídos

Fevereiro : é codificado para 28 dias. Anos bissextos são um inferno a acrescentar, na verdade. se alguém tiver uma idéia que não adicione 10 linhas de código, vá em frente e poste, então eu a adiciono ao meu código.

época : não levei muito tempo em consideração, pois a necessidade é excluir arquivos anteriores a uma determinada data; levar horas / minutos teria excluído os arquivos de um dia destinado a ser mantido.

LIMITAÇÃO

época assume como certo que o seu formato de data curta é AAAA-MM-DD. Ele precisaria ser adaptado para outras configurações ou para uma avaliação em tempo de execução (leia sShortTime, configuração vinculada ao usuário, configure a ordem de campo adequada em um filtro e use o filtro para extrair os dados corretos do argumento).

Eu mencionei que odeio a formatação automática deste editor? remove as linhas em branco e a copiar e colar é um inferno.

Eu espero que isso ajude.

Jay
fonte
4
+1 para código não destrutivo, tentarei manter isso em mente quando eu mesmo fornecer exemplos de código.
Haugholt 10/09/14
5
Muito tarde e totalmente sem relação, mas 7daysclean.cmdsoa como um nome incrível para uma banda de Synth-Punk.
Flonk
21
forfiles /p "v:" /s /m *.* /d -3 /c "cmd /c del @path"

Você deve fazer /d -3(3 dias antes) Isso funciona bem para mim. Portanto, todos os lotes complicados podem estar na lixeira. Também forfilesnão suporta caminhos UNC; portanto, faça uma conexão de rede com uma unidade específica.

Arno Jansen
fonte
3
Mesmo erro que em outras respostas (incluindo a aceita). De onde veio esse estranho hábito de especificar *.*? O curinga *.*não corresponde a todos os arquivos no Windows. Ele corresponde apenas aos arquivos .em seus nomes. O OP nunca disse nada sobre a exigência .no nome do arquivo. O parâmetro apropriado é /M *, mas esse é o padrão de qualquer maneira. Não é necessário /M.
AnT
@ Ant: Você tem certeza? Eu acho que *.*se comporta exatamente como *no Windows. Na verdade, eu apenas tentei no meu computador e, na dir *.*verdade test file, listei um arquivo sem extensão.
Andreas Rejbrand
1
@ Andréas Rejbrand: Acho surpreendente também, mas o tratamento de *.*é inconsistente entre, digamos, dire a -mopção de forfiles. A -m *.*máscara realmente ignorará nomes de arquivos sem extensão, como afirmei no meu comentário acima (tente você mesmo). A parte engraçada aqui é que a documentação da MS afirma explicitamente que -m *.*é o padrão. No entanto, se você tentar na vida real, verá que o padrão é realmente -m *- todos os arquivos são iterados.
AnT
1
Esta página de documentação da Microsoft - technet.microsoft.com/en-us/library/cc753551(v=ws.11).aspx - contém um grande número de erros (por exemplo, em exemplos) porque o autor do documento acreditava incorretamente que a *.*máscara era aplicada para todos os arquivos. Minha pergunta retórica sobre "esse estranho hábito" foi de fato desnecessária, uma vez que tratar *.*e *como equivalente é uma convenção antiga do Windows. No entanto, a /mopção in forfilesviola essa convenção por algum motivo.
AnT
1
Informações
básicas
18

Dê uma olhada na minha resposta para uma pergunta semelhante :

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"

Isso exclui arquivos anteriores a uma determinada data. Tenho certeza de que pode ser modificado para voltar sete dias a partir da data atual.

atualização: Percebo que o HerbCSO melhorou no script acima. Eu recomendo usar a versão dele .

e.James
fonte
12

Meu comando é

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

@PATH - é apenas caminho no meu caso, então eu tive que usar @PATH\@FILE

também forfiles /?não está funcionando para mim também, masforfiles (sem "?") funcionou bem.

E a única pergunta que tenho: como adicionar várias máscaras (por exemplo " .log | .bak")?

Tudo isso sobre o forfiles.exe que eu baixei aqui (no win XP)

Mas se você estiver usando o servidor do Windows, o forfiles.exe já deve estar lá e difere da versão ftp. É por isso que devo modificar o comando.

Para o Windows Server 2003, estou usando este comando:

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

Muitas vezes, existem questões relacionadas a data / hora relativas a serem resolvidas com o arquivo em lote. Mas o interpretador de linha de comando cmd.exe não tem função para cálculos de data / hora. Muitas boas soluções de trabalho usando scripts ou aplicativos de console adicionais já foram postadas aqui, em outras páginas do Stack Overflow e em outros sites.

Comum para operações baseadas em data / hora é o requisito de converter uma sequência de data / hora em segundos desde um determinado dia. Muito comum é 1970-01-01 00:00:00 UTC. Mas qualquer dia posterior também pode ser usado, dependendo do período necessário para dar suporte a uma tarefa específica.

Jay postou 7daysclean.cmd contendo uma solução rápida de "data para segundos" para o interpretador de linha de comando cmd.exe . Mas não leva em consideração os anos bissextos corretos. JR postou um complemento por considerar o dia bissexto no ano corrente, mas ignorando os outros anos bissextos desde o ano base, ou seja, desde 1970.

Uso há 20 anos tabelas estáticas (matrizes) criadas uma vez com uma pequena função C para obter rapidamente o número de dias, incluindo os dias bissextos de 01-01-2009 nas funções de conversão de data / hora em meus aplicativos escritos em C / C ++.

Esse método de tabela muito rápido também pode ser usado no código de lote usando o comando FOR . Por isso, decidi codificar a sub-rotina em lote GetSecondsque calcula o número de segundos desde 1970-01-01 00:00:00 UTC para uma sequência de data / hora passada para essa rotina.

Nota: Os segundos bissextos não são levados em consideração, pois os sistemas de arquivos do Windows também não suportam segundos bissextos.

Primeiro, as tabelas:

  1. Dias desde 1970-01-01 00:00:00 UTC para cada ano, incluindo os dias bissextos.

    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
    

    O cálculo dos segundos para o ano 2039 a 2106 com a época começando em 01-01-2009 só é possível com o uso de uma variável de 32 bits não assinada, ou seja, sem assinatura longa (ou sem assinatura int) em C / C ++.

    Mas o cmd.exe usa para expressões matemáticas uma variável de 32 bits assinada. Portanto, o valor máximo é 2147483647 (0x7FFFFFFF), que é 2038-01-19 03:14:07.

  2. Informações sobre o ano bissexto (Não / Sim) para os anos de 1970 a 2106.

    1970 - 1989: N N Y N N N Y N N N Y N N N Y N N N Y N
    1990 - 2009: N N Y N N N Y N N N Y N N N Y N N N Y N
    2010 - 2029: N N Y N N N Y N N N Y N N N Y N N N Y N
    2030 - 2049: N N Y N N N Y N N N Y N N N Y N N N Y N
    2050 - 2069: N N Y N N N Y N N N Y N N N Y N N N Y N
    2070 - 2089: N N Y N N N Y N N N Y N N N Y N N N Y N
    2090 - 2106: N N Y N N N Y N N N N N N N Y N N
                                     ^ year 2100
    
  3. Número de dias para o primeiro dia de cada mês no ano atual.

                       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
    

Converter uma data em um número de segundos desde 01-01-2009 é bastante fácil usando essas tabelas.

Atenção por FAVOR!

O formato das seqüências de data e hora depende das configurações de idioma e região do Windows. Os delimitadores e a ordem dos tokens atribuídos às variáveis ​​de ambiente e Day, no primeiro loop FOR, devem ser adaptados ao formato local de data / hora, se necessário.MonthYearGetSeconds

É necessário adaptar a sequência de datas da variável de ambiente se o formato de data na variável de ambiente DATE for diferente do formato de data usado pelo comando FOR ativado %%~tF.

Por exemplo, quando %DATE%expande para Sun 02/08/2015enquanto %%~tFexpande para 02/08/2015 07:38 PMo código abaixo, pode ser usado com a modificação da linha 4 para:

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

Isso resulta na passagem para a sub-rotina just 02/08/2015- a sequência de datas sem as três letras da abreviação do dia da semana e o caractere de espaço separador.

Como alternativa, pode-se usar o seguinte para passar a data atual no formato correto:

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

Agora, os últimos 10 caracteres da sequência de datas são passados ​​para a função GetSecondse, portanto, não importa se a sequência de datas da variável de ambiente DATE está com ou sem dia da semana, desde que dia e mês estejam sempre com 2 dígitos na ordem esperada, ou seja, no formato dd/mm/yyyy ou dd.mm.yyyy.

Aqui está o código do lote com a explicação dos comentários que apenas gera qual arquivo excluir e qual arquivo manter na C:\Tempárvore de pastas, consulte o código do primeiro loop 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 /A-D-H-S /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 ("Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N") 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 ("N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N N Y N N") 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 um desempenho ideal, seria melhor remover todos os comentários, ou seja, todas as linhas começando com rem após 0-4 espaços à esquerda.

E as matrizes também podem ser menores, ou seja, diminuindo o período de 1980-01-01 00:00:00 para 2038-01-19 03:14:07 conforme atualmente suportado pelo código de lote acima, por exemplo, para 2015-01 -01 a 2019-12-31, como o código abaixo usa, que realmente exclui arquivos com mais de 7 dias na C:\Tempárvore de pastas.

Além disso, o código do lote abaixo é otimizado para o 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 /A-D-H-S /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 ("N Y N N N") 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 obter mais informações sobre formatos de data e hora e comparações de horário de arquivo no Windows, consulte minha resposta em Descubra se o arquivo tem mais de 4 horas no arquivo em lotes com muitas informações adicionais sobre horários de arquivo.

Mofi
fonte
9

Copie esse código e salve-o como DelOldFiles.vbs.

USO EM CMD: cscript //nologo DelOldFiles.vbs 15

15 significa excluir arquivos com mais de 15 dias no passado.

  '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<ThresholdDate Then
        Wscript.StdOut.WriteLine f1
        f1.Delete
        n = n + 1    
     End If
       Next
       Wscript.StdOut.WriteLine "Deleted " & n & " file(s)."
    End Function

    If Not WScript.FullName = WScript.Path & "\cscript.exe" Then
      WScript.Echo "USAGE ONLY IN COMMAND PROMPT: cscript DelOldFiles.vbs 15" & vbCrLf & "15 means to delete files older than 15 days in past."
      WScript.Quit 0   
    End If

    DeleteOlderFiles(".")
 'to here
Paris
fonte
8

Para o Windows Server 2008 R2:

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

Isso excluirá todos os arquivos .sql com mais de 90 dias.

NotJustClarkKent
fonte
7

Use forfiles.

Existem versões diferentes. Os primeiros usam parâmetros de estilo unix.

Minha versão (para servidor 2000 - observe que não há espaço após as opções) -

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

Para adicionar forfiles ao XP, obtenha o exe em ftp://ftp.microsoft.com/ResKit/y2kfix/x86/

e adicione-o a C: \ WINDOWS \ system32

Aidan Ewen
fonte
11
@ Kibbee não é a mesma resposta, a sintaxe é diferente. Esta foi uma tentativa geniuna de ajudar. Conhecendo muito pouco a linha de comando do Windows, passei horas de frustração fazendo com que isso funcionasse. As principais informações para mim foram o fato de que existem versões diferentes (com sintaxe diferente) e que eu precisava remover os espaços. Nenhuma dessas coisas foi incluída na resposta original. (Eu teria comentado sobre a resposta, mas eu não tenho os privilégios
Aidan Ewen
Mesmo erro que em outras respostas (incluindo a aceita). Curinga *.*em forfilesnão corresponder todos os arquivos. Ele corresponde apenas aos arquivos .em seus nomes (ou seja, arquivos com extensões). O OP nunca disse nada sobre a exigência .no nome do arquivo. O parâmetro apropriado é /M *, mas esse é o padrão de qualquer maneira. Não é necessário /M.
AnT
7

Para o Windows 2012 R2, o seguinte funcionaria:

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

para ver os arquivos que serão excluídos, use este

    forfiles /p "c:\FOLDERpath" /d -30 /c "cmd /c echo @path @fdate"
Viktor Ka
fonte
Isso funciona mesmo no Windows Server 2008 e acima e Windows 7.
kwrl
6

Na IMO, o JavaScript está gradualmente se tornando um padrão de script universal: provavelmente está disponível em mais produtos do que em qualquer outra linguagem de script (no Windows, está disponível usando o Windows Scripting Host). Eu tenho que limpar arquivos antigos em muitas pastas, então aqui está uma função JavaScript para fazer isso:

// 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 cada pasta seja limpa, basta adicionar outra chamada à função clearFolder (). Esse código específico também preserva os arquivos exe e dll e limpa as subpastas também.

Graham Laight
fonte
6

Que tal essa modificação no 7daysclean.cmd para levar em consideração um ano bissexto?

Isso pode ser feito em menos de 10 linhas de codificação!

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:

A condição acima contribuída por JR é sempre avaliada como falsa devido à sintaxe inválida.

E Month GEQ 2também está errado porque a adição de 86400 segundos por mais um dia deve ser feita em um ano bissexto apenas nos meses de março a dezembro, mas não em fevereiro.

Um código de trabalho para levar em consideração o dia bissexto - apenas no ano atual - no arquivo em lote 7daysclean.cmd publicado por Jay seria:

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
JR
fonte
5

Posso acrescentar uma contribuição humilde a esse segmento já valioso. Estou descobrindo que outras soluções podem se livrar do texto de erro real, mas estão ignorando o% ERRORLEVEL% que sinaliza uma falha no meu aplicativo. E eu legitimamente quero% ERRORLEVEL% desde que não seja o erro "Nenhum arquivo encontrado".

Alguns exemplos:

Depurando e eliminando o erro especificamente:

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

Usando um oneliner para retornar ERRORLEVEL com êxito ou falha:

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

Usando um oneliner para manter o ERRORLEVEL em zero para obter sucesso no contexto de um arquivo em lote no meio de outro código (ver> nul redefine o 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 uma etapa do trabalho CmdExec do SQL Server Agent, cheguei ao seguinte. Não sei se é um erro, mas o CmdExec na etapa reconhece apenas a primeira linha 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%
Picada
fonte
5

Caramba, muitas respostas já. Uma rota simples e conveniente que encontrei foi executar o ROBOCOPY.EXE duas vezes em ordem sequencial a partir de uma única instrução de linha de comando do Windows usando o parâmetro & .

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

Neste exemplo, ele funciona escolhendo todos os arquivos ( . ) Com mais de 30 dias e movendo-os para a pasta de destino. O segundo comando faz o mesmo novamente com a adição do comando PURGE, o que significa remover arquivos na pasta de destino que não existem na pasta de origem. Portanto, essencialmente, o primeiro comando MOVE arquivos e o segundo DELETE porque eles não existem mais na pasta de origem quando o segundo comando é chamado.

Consulte a documentação do ROBOCOPY e use a opção / L ao testar.

GBGOLC
fonte
4

Se você tiver o kit de recursos do XP, poderá usar o robocopy para mover todos os diretórios antigos em um único diretório e, em seguida, use o rmdir para excluir apenas um:

mkdir c:\temp\OldDirectoriesGoHere
robocopy c:\logs\SoManyDirectoriesToDelete\ c:\temp\OldDirectoriesGoHere\ /move /minage:7
rmdir /s /q c:\temp\OldDirectoriesGoHere
neuracnu
fonte
4

Acho que a resposta do e.James é boa, pois funciona com versões não modificadas do Windows desde o Windows 2000 SP4 (e possivelmente anterior), mas era necessário gravar em um arquivo externo. Aqui está uma versão modificada que não cria um arquivo de texto externo, mantendo a compatibilidade:

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 à pergunta original, aqui está um script que faz TODAS as contas para você, se você o chamar com o número de dias 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: O código acima leva em consideração os anos bissextos, bem como o número exato de dias em cada mês. O único máximo é o número total de dias desde 0/0/0 (depois disso, retorna anos negativos).

OBSERVAÇÃO: A matemática é apenas uma maneira; não é possível obter corretamente datas futuras com informações negativas (tentará, mas provavelmente passará o último dia do mês).

Lectrode
fonte
3

Você pode conseguir isso. Você pode dar uma olhada nesta pergunta , para um exemplo mais simples. A complexidade vem quando você começa a comparar as datas. Pode ser fácil dizer se a data é maior ou não, mas há muitas situações a serem consideradas se você realmente precisar obter a diferença entre duas datas.

Em outras palavras - não tente inventar isso, a menos que você realmente não possa usar as ferramentas de terceiros.

Paulius
fonte
3

isso não é nada surpreendente, mas eu precisava fazer algo parecido hoje e executá-lo como tarefa agendada etc.

arquivo em lote, DelFilesOlderThanNDays.bat abaixo, com exemplo de execução com parâmetros:

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
Goran B.
fonte
3

Expandindo a resposta do aku, vejo muitas pessoas perguntando sobre os caminhos UNC. O simples mapeamento do caminho unc para uma letra de unidade fará com que os arquivos sejam felizes. O mapeamento e o mapeamento de unidades podem ser feitos programaticamente em um arquivo em lotes, por exemplo.

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"

Isso excluirá todos os arquivos com uma extensão .gz com mais de 7 dias. Se quiser garantir que o Z: não esteja mapeado para mais nada antes de usá-lo, você pode fazer algo simples como

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!"
)
Tobias Järvelöv
fonte
3

O ROBOCOPY funciona muito bem para mim. Originalmente sugeriu meu Iman. Mas, em vez de mover os arquivos / pastas para um diretório temporário e excluir o conteúdo da pasta temporária, mova os arquivos para o lixo !!!

Esta é algumas linhas do meu arquivo em lotes de backup, por exemplo:

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

Ele limpa qualquer coisa com mais de 15 dias da minha pasta 'Temp' e 30 dias para qualquer coisa na minha pasta de backup do AutoCAD. Uso variáveis ​​porque a linha pode ficar bastante longa e posso reutilizá-las para outros locais. Você só precisa encontrar o caminho dos para sua lixeira associada ao seu login.

Este é um computador de trabalho para mim e funciona. Entendo que alguns de vocês podem ter direitos mais restritivos, mas tente de qualquer maneira;) Pesquise no Google explicações sobre os parâmetros do ROBOCOPY.

Felicidades!

Shawn Pauliszyn
fonte
1
A segunda resposta mais votada existente também adota o RoboCopy. Concedido, você usa opções um pouco diferentes, mas está perto da resposta existente.
Jonathan Leffler
2
Esta publicação possui um novo conteúdo - movendo arquivos para a lixeira. Isso ainda não foi proposto com o Robocopy e responde totalmente à postagem original usando um comando de linha única interno. Funciona melhor do que combinar os comandos 'DEL' e 'RMDIR' porque os arquivos podem ser restaurados a partir da lixeira. Ei, eu ainda sou novo aqui - me dar uma pausa;)
Shawn Pauliszyn
2

Meu script para excluir arquivos anteriores a um ano específico:

@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 /a-d-h-s /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
efdummy
fonte
1

Este fez isso por mim. Ele funciona com uma data e você pode subtrair a quantia desejada em anos para voltar no tempo:

@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

o /Fnocmd /c del @path /F forças o arquivo específico a ser eliminado em alguns casos, o arquivo pode ser somente leitura.

o dateYearé o ano variável e aí você pode alterar o substrato de acordo com suas próprias necessidades

Snickbrack
fonte