I'm writing a Windows batch file that will purge logs older than 90 days. How can I concatenate the outputs of the commands so that they appear in one line? I also want to append this output to a file later. My batch file so far is:
#echo off
time /t && echo "--" && date /t && echo " -I- Purging " && FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"
rem FORFILES that will purge the files
and this outputs:
12:08
--
14/08/2012
-I- Purging
"<filename>" 02/08/2012
"<filename>" 30/07/2012
How can I concatenate these outputs? Thanks.
Putting everything on one line except for the FOR output is easy. You just need to use
the dynamic %TIME% and %DATE% variables instead of the TIME and DATE commands
#echo off
echo %time% "--" %date% -I- Purging
FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"
rem FORFILES that will purge the files
If you also want the file names to appear on the same line, then you can use a temp variable as EitanT suggested. But that limits the number of files to what can fit in the max 8191 variable size. To process an unlimited number of files you can use SET /P instead. It doesn't seem like the FOR /F statement should be necessary, but there is a quoting issue that I couldn't solve without it.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
for /f "delims=" %%A in (
'FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate"'
) do set/p="%%A "
)
rem FORFILES that will purge the files
There is no reason not to purge the files at the same time that you are listing them. Since FORFILES is so slow, it would be much more efficient to purge and list in the same command.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
for /f "delims=" %%A in (
'FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c del #path&echo #file #fdate"'
) do set/p="%%A "
)
Update 2015-01-06
I figured out a solution without using FOR /F. I use 0x22 to enclose the SET /P prompt in quotes, and I use FINDSTR to eliminate the empty line that FORFILES writes before any requested output.
#echo off
<nul (
set/p="%time% -- %date% -I- Purging "
forfiles /p "d:\logs" /m *.log /d -90 /c "cmd /c del #path&set/p=0x5e0x22#file #fdate 0x5e0x22"'|findstr .
)
If you want to concatenate the lines in the output, you can set CMD_STR to be your desired command, and use a for /f loop, like so:
#echo off
setlocal enabledelayedexpansion
set "CMD_STR=time /t && echo "--" && date /t && echo " -I- Purging " && FORFILES /P "D:\Logs" /M *.log /D -90 /C "cmd /c echo #file #fdate""
set CONCAT_STR=
for /f %%i in ('%CMD_STR%') do set "CONCAT_STR=!CONCAT_STR! %%i"
echo !CONCAT_STR!
The loop iterates through the lines of the output and appends them one by one to CONCAT_STR.
Related
This question already has answers here:
How to read file contents into a variable in a batch file?
(4 answers)
Closed 2 years ago.
I'm trying to find all files older than 30 days in several directories using this command:
[Directory] && forfiles /d -30 /c "cmd /c echo #path"
The output is a .txt file.
The text file contains the path to the directory:
C:\Directory1
C:\Directory2
C:\Directory3 etc
I'm trying to loop through several directories using a text file but I need to provide 2 commands:
cd (to change to the directory whose files I need info on) and the actual command to get the information)
If I create a batch file entering the directory names manually I have something like this:
cd "C:Directory1" && forfiles /d -30 /c "cmd /c echo #path"
cd "C:Directory2" && forfiles /d -30 /c "cmd /c echo #path"
cd "C:Directory3" && forfiles /d -30 /c "cmd /c echo #path"
How do I enter the "cd" command at the beggining of the loop, then the directory which is in the txt file and the rest of the command (forfiles /d -30 /c "cmd /c echo #path")
What I have so far is:
for /f "usebackq tokens=*" %%A in ("C:\list.txt") do forfiles /d -30 /c "cmd /c echo #path %%A
Thanks!
The FOR command with the /F option is used to read a file and it assigns each iteration of the file to the variable %%A. So use the that variable with the /P option of the FORFILES command.
for /f "usebackq tokens=*" %%A in ("C:\list.txt") do forfiles /P "%%~A" /d -30 /c "cmd /c echo #path"
I have a batch file that I call via SQL server agent.
Currently I have:
ForFiles /p "C:\folder\subfolder" /d -7 /c "cmd /c del #file"
The command works fine if there is a file older than 7days.
If there isn't a file that meets the criteria then the script fails causing SQL to report failure.
Ideally I need to add and if statement that if a file exists that is older than 7 days then carry out the delete command otherwise ignore it.
Any guidance?
Wrap it inside a FOR /F command an redirect standard error to NUL.
FOR /F "delims=" %%G IN ('forfiles /P "C:\folder\subfolder" /d -7 2^>nul') do DEL "%%~G"
Try this:
cd /d C:\folder\subfolder
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "C:\folder\subfolder" -s -m *.* -d -7 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET errorlevel=0
) ELSE (
SET errorlevel=1
)
IF "%_CmdResult%" == "NONE" SET errorlevel=0
it checks for an error, and will put the error into a variable and then we set the errorlevel to zero if found.
I have a Windows batch file that processes a bunch of files. As part of this I use the following line:
forfiles /p "%~dpn1%LogDir%" /m "%SupportLog%*" /c "cmd /c logreader.py #file > \"%~dpn1%ParsedLogDir%\#file_Logreader.txt\"
This works OK, but essentially loops through all my files (%SupportLog%*) and passes each one by one into the logreader.py script.
What I really want to do is to create a list or parameter of all these files and pass all of them at once into the Python script, such the command that should be run would resemble:
logreader.py "logfile.log" "logfile.log.1" "logfile.log.3" .....
I tried to use the set command within the forfiles command like that:
forfiles /p "%~dpn1%LogDir%" /m "%SupportLog%*" /c "cmd /c set PARAMS=%PARAMS%#file "
However, when run this and leave the ECHO ON, I see:
forfiles /p "C:\Path\log" /m "logfile.log*" /c "cmd /c set PARAMS=#file "
This is incorrect. And when I do echo %PARAMS%, I get no result.
Is there a way of achieving this?
forfiles is complicated or may even be buggy. It does not support the quotes within /c "cmd /c ...." must be replaced by hex characters 0x22, or by \".
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ('dir /b /A-D "logfile.log.?"') do (
set _logfile=!_logfile! "%%a"
)
echo logreader.py %_logfile%
output:
logreader.py "logfile.log.1" "logfile.log.2" "logfile.log.3" "logfile.log.4" "logfile.log.5"
forfiles creates a new cmd instance per each loop iteration, so a variable PARAMS only exists as long as the containing instance.
If you want to use forfiles and work around that issue, you might do the following:
rem collect all items in a temporary file as a single line:
del /F /Q "loglist.tmp" 2> nul
forfiles /P "%~dpn1%LogDir%" /M "%SupportLog%*" /C "cmd /C echo | set /P _=\"#file \" >> \"loglist.tmp\""
rem pass over the content of temporary file as parameters:
for /F "usebackq eol=| delims=" %%L in ("loglist.tmp") do logreader.py %%L
rem clean up temporary file:
del /F /Q "loglist.tmp" 2> nul
Relying on a variable for collecting the parameters rather than a temporary file, the following could work:
rem collect all items in a variable:
set "PARAMS="
setlocal EnableDelayedExpansion
for /F "delims=" %%L in (
'forfiles /P "%~dpn1%LogDir%" /M "%SupportLog%*" /C "cmd /C echo.#file"'
) do set "PARAMS=!PARAMS!%%L "
endlocal & set "PARAMS=%PARAMS%"
rem pass over the content of variable as parameters:
if defined PARAMS logreader.py %PARAMS%
Thanks all. I eventually used a combination of the pointers listed above (and elsewhere) to come up with the following (an excerpt of the full batch file):
setlocal EnableDelayedExpansion
Set SupportLog=support.log
Set LogDir=\var\log
set PARAMS=
for %%A in ("%~dpn1%LogDir%\%SupportLog%*") do (
set PARAMS=!PARAMS! "%%A"
)
logreader.py %PARAMS% > "%~dpn1%ParsedLogDir%\%SupportLog%_Logreader.txt"
So, the a shortcut to the batch file is created in the "SendTo" folder, I can then right click the zipped log file and "SendTo" the batch, and all works as expected.
Perhaps I should switch to PowerShell for things like this going forward.
Is there away to only display files in a folder called "Export", when such a subfolder is in multiple locations?
This will display all .xlsx files below where I'm running the batch file from, however I don't want to display all, I only want to display things in a folder called "Export":
forfiles -p "%~dp0\" -s -m *.xlsx -d -365 -c "cmd /c ECHO #relpath"
I have attempted things like:
forfiles -p "%~dp0\*\Export" -s -m *.xlsx -d -365 -c "cmd /c ECHO #relpath"
However it doesn't recognise syntax like this. Says invalid argument/option, but they are valid until I add *\ to the path.
This is an example of the structure I'm working with and what I what results to display:
%~dp0\1\Exports\Excel\ - (Do display .xlsx files)
%~dp0\1\Do Not Delete\Excel\ - (Don't display .xlsx files)
%~dp0\2\Exports\Excel\ - (Do display .xlsx files)
%~dp0\2\Do Not Delete\Excel\ - (Don't display .xlsx files)
The number folders would be a variable somehow, which is why the *\ is in my attempt.
I will then edit this to delete the files when I know it's picking up the right ones.
You could use two nested forfiles loops:
forfiles /S /P "%~dp0\" /M "Export*" /C "cmd /C if #isdir==TRUE forfiles /P #path\Excel /M *.xlsx /D -365 /C 0x22cmd /C echo 00x7840path0x22"
This command line walks through the given root directory (%~dp0) recursively and iterates through all items matching Export* (you might replace this pattern by Export or Exports, depending on your needs); the hierarchy depth is not checked in order to avoid the need of three nested loops. Anyway, if the iterated item is a directory, the sub-directory Excel is enumerated and searched for items matching *.xlsx.
Supplement:
The above command line may display many error messages like ERROR: The specified directory does not exist. or ERROR: Files of type "*.xlsx" not found., depending on your data; to avoid them, redirect STDERR to the nul device:
2> nul forfiles /S /P "%~dp0\" /M "Export*" /C "cmd /C if #isdir==TRUE forfiles /P #path\Excel /M *.xlsx /D -365 /C 0x22cmd /C echo 00x7840path0x22"
The forfiles command returns an empty line to STDOUT, so the above command line will contain many of them; to avoid such too, redirect STDERR and STDOUT to the nul device and redirect only the wished data to the console con:
1> nul 2>&1 forfiles /S /P "%~dp0\" /M "Export*" /C "cmd /C if #isdir==TRUE forfiles /P #path\Excel /M *.xlsx /D -365 /C 0x22cmd /C 1> con echo 00x7840path0x22"
You can send results with | to FINDSTR like this:
FORFILES /P . /S /M *.xlsx /D -365 /C "CMD /C ECHO #path" | ^
FINDSTR "\\Exports\\" | ^
FINDSTR /I /C:Delete /V
So you search every xlsx files located in a certain path (here is current dir) Every xlsx must contain Exports as folder and show every xlsx that doesn't (/V Displays only the non-matching lines) contain "delete" (/I mean insensitive)
In a batch file you can replace /P . by any variable path /P "%~dp0" /P "%CD%" or send as argument /P %1
Also you can do FOR loop to make any command for every match.
In one line to put in console:
FOR /F "usebackq delims=" %A in (`forfiles /p . /s /m *.xlsx /D -365 /C "cmd /c ECHO #path" ^|FINDSTR "\\Exports\\" ^|FINDSTR /I /C:Delete /V`) DO #ECHO FAKE-COMMAND %A
In a batch file:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "usebackq delims=" %%A in (
`forfiles /p . /s /m *.xlsx /D -365 /C "cmd /c ECHO #path" ^|FINDSTR "\\Exports\\" ^|FINDSTR /I /C:Delete /V`
) DO (
SET "myfile=%%~A"
#ECHO FAKE-COMMAND "!myfile!"
)
ENDLOCAL
GOTO :EOF
I want to do some stuffs on images with extensions defined in a variable.
The following script run well:
set AllowExt="jpg png bmp"
forfiles /p D:\Pictures /m *.* /c "cmd /c if not %AllowExt:jpg=% == %AllowExt% echo #file
But the following script throws error
set AllowExt="jpg png bmp"
forfiles /p D:\Pictures /m *.* /c "cmd /c if not %AllowExt:#ext=% == %AllowExt% echo #file"
ERROR: Invalid argument/option - 'png'.
Type "FORFILES /?" for usage.
You might try this:
set "AllowExt=.jpg .png .bmp"
for %%a in (%AllowExt%) do (
forfiles /p D:\Pictures /m *%%a /c "cmd /c echo #file"
)
"cmd /c echo #file" is the default command, see forfiles /?.
This should get you the filenames you need.
#echo off
set "AllowExt=jpg png bmp"
set "AllowExt= %AllowExt%"
set "AllowExt=%AllowExt: = *.%"
pushd "D:\Pictures"
for %%a in (%AllowExt%) do (
echo "%%a"
)
popd