Howto batch-convert pdf's using ImageMagick and benchmark the process? - windows

After fiddling around for the first time with the command-line and ImageMagic, I have been able to do the following:
c:\test\paper.pdf contains a pdf file
c:\test>convert paper.pdf output-%d.tiff
The pdf file contains five pages, and the output is as expected 5 tiff files :-) Now I want to put multiple files in c:\test and loop through them, creating pages based on the original filename. So assume the following files in c:\test:
paper.pdf (5 pages)
example.pdf (2 pages)
new.pdf (1 page)
The output of a batch script should be 8 tiff files in the c:\test\tiffs\ folder:
paper-0.tiff paper-1.tiff paper-2.tiff paper-3.tiff paper-4.tiff
example-0.tiff example-1.tiff
new-0.tiff
and I would very much like the command line return the time it took converting the pdf's to tiffs:
c:\test>convert.bat
c:\test>This action took 120 seconds
I'm trying to write this batch file but since it's my first try I'm having trouble. I am first trying to loop through the test folder and create all the tiffs (leaving timing the process for now):
for %%f in (C:\test\) do convert %%f.pdf %%f-%%d.tiff
I would have expected this to work but got an error:
C:\Users\Me>convert c:\test\c:\test\.pdf c:\test\c:\test\-%d.tiff convert.exe:
unable to open image `c:\test\c:\test\.pdf': Invalid argument #
error/blob.c/OpenBlob/2646. convert.exe: no decode delegate for this image
format `c:\test\c:\test\.pdf' # error/constitute.c/ReadImage/552. convert.exe:
no images defined `c:\test\c:\test\-%d.tiff' #
error/convert.c/ConvertImageCommand/3106.
What am I doing wrong?

Timing
This is one of those jobs that it is just wrong to do (for real—it's OK to do it to show off) with batch/CMD. Nearly every programming language I've ever encountered would be better at doing this than CMD.
This particular (hackish) implementation will not work if the elapsed time goes past midnight of any given day, and depending on how you have dates configured in your locale, it might break if the run starts before 1 P.M. and finishes after 1 P.M.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=5 delims= " %%t IN ('^<NUL time') DO SET START_TIME=%%t
:: Stuff that you're timing.
FOR /F "tokens=5 delims= " %%t IN ('^<NUL time') DO SET END_TIME=%%t
SET ELAPSED=
FOR /F "delims=:. tokens=1,2,3,4" %%a IN ("%START_TIME%") DO (
FOR /F "delims=:. tokens=1,2,3,4" %%n IN ("%END_TIME%") DO (
SET /A HOURS=%%n-%%a
SET /A MINUTES=%%o-%%b
SET /A SECONDS=%%p-%%c
SET /A HUND=%%q-%%d
SET /A TOTAL=!HOURS!*3600 + !MINUTES!*60 + !SECONDS!
IF !HUND! LSS 0 (
SET /A TOTAL=!TOTAL!-1
SET /A HUND=!HUND!+100
)
IF !HUND! LSS 10 SET HUND=0!HUND!
SET ELAPSED=!TOTAL!.!HUND!
)
)
#ECHO.
#ECHO.
#ECHO This action took %ELAPSED% seconds.
ImageMagick
The problem I see with your convert command is that you've not told CMD what pattern to search for. It sounds like you want *.pdf. Doing this will mean that you don't need to append ".pdf" to the end of the input file name and need to remove the extension when specifying the output file name. The %%~dpnf produces the entire path to the file without the extension.
FOR %%f IN (C:\temp\*.pdf) do convert "%%~f" "%%~dpnf-%%d.tiff"
EDIT: To put the PDF's in a different location, change the %%f substitution:
FOR %%f IN (C:\temp\*.pdf) do convert "%%~f" "\path\to\tiffs\%%~nf-%%d.tiff"
Better Method?
I guess I should at least put in a plug for doing it the Rightâ„¢ way. If you didn't want to invoke your command from a script in a language that supported timing (like Python), you could just retrieve the system time as an integer. This strategy will work until roughly 2038. I used Perl in this example.
#ECHO OFF
FOR /F %%t IN ('perl -e "print(time());"') DO SET START_TIME=%%t
:: stuff to time
FOR /F %%t IN ('perl -e "print(time());"') DO SET END_TIME=%%t
SET /A ELAPSED=%END_TIME%-%START_TIME%
#ECHO This step took %ELAPSED% seconds.

Related

Process cannot access the file as it is used by another process

I am trying to create windows batch script to check the size of file after it is downloaded but it fails with this message.
The Process cannot access the file as it is used by another process
2048 was unexpected at this time.
Thanks in advance for any help.
#echo off
d:\wget.exe ...
Rem this wget process downloads tst_file.xml
set file="tst_file.xml"
set maxbytesize=2048
for /F "usebackq" %%A IN ('%file%') DO set size=%%~zA
if %size% LSS %maxbytesize% (echo file is less than 2KB)
Consider this alteration.
#echo off
d:\wget.exe ...
Rem this wget process downloads tst_file.xml
set "file=tst_file.xml"
set maxbytesize=2048
if not exist "%file%" (
#echo '%file%' does not exist.
goto :SKIP_CHECK
)
for /F "usebackq" %%A IN ('%file%') DO set size=%%~zA
if %size% LSS %maxbytesize% (echo file is less than 2KB)
:SKIP_CHECK
Notes
If the file doesn't exist, the FOR loop will not be able to get the file size, and the LSS comparison will blow up with no operand to compare.
If you had quoted each of the vars (e.g. if "%size%" LSS "%maxbytesize%" ...), it wouldn't have blown up. However, it would have performed a lexical comparison, which when compared to anything would evaluate to true.
In batch, if you use double quotes around only the value in a SET command, the quotes will be included in the value. Even though it looks unorthodox, put quotes around the whole <variable>=<value> expression.
If you skip tokenization and get the entire line (i.e. tokens=*), you'll be able to handle files with spaces in the name.

Display output in a certain way using echo of batch

I am working on a program using batch where the program read root directories from a text file and count total number of Folders and Files in all th give root directories. The program is working as it should be but I want to display the output in a certain way.
This is how I want to display output
0 : OF : 6
The first value should change each time program finish counting in one root directory. I have written code for it but the output I am getting is this.
Here is code I have written to change it.
:textUpdate
echo !counter! : OF : %number%
GOTO :EOF
where counter is the current number of root directory and number is total number of directories found in the text file. Is there any way to display the output like the first one.
you can abuse set /p to write to screen. It doens't append a line feed. You also need a Carriage Return to go back to the beginning of the line to overwrite the old output:
#echo off
setlocal EnableDelayedExpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
for /l %%n in (1,1,6) do (
set /P "=Count %%n of 6!CR!" <nul
timeout 1 >nul
)
The first for /f loop is just to get a CR (Carriage Return). You have to use delayed expansion to be able to use it (%CR% does not work).

Delete text every n seconds in windows

I have a .txt file that updates every five seconds.
22:37:03
Some text 1
22:37:08
Some text 2
Some text 3
22:37:13
Some text 4
I have a program that reads this data from the Start of the file, which I'd like to be as up to date as possible (to within 5 seconds if possible).
If I read the file directly with the program it starts from the earliest entry, which may be up to two hours behind (depending on when I started writing the file).
Is there a command I could add to a batch file that would delete any data that was say 8 seconds older than the current system time?
I already run a command that deletes the first 2 lines every 5 seconds, but the number of lines under the time stamp vary.
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
SET "destdir=U:\destdir"
SET "filename1=%sourcedir%\q42253025.txt"
SET "outfile=%destdir%\outfile.txt"
FOR /f "usebackqdelims=" %%a IN ("%filename1%") DO (
FOR /f "tokens=1-3delims=:" %%p IN ("%%a") DO (
IF "%%r"=="" (>>"%outfile%" ECHO %%a) ELSE (>"%outfile%" ECHO %%a)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
I used a file named q42253025.txt containing your data for my testing.
Assuming filename1 is your logfile, then this code will read that file and create a new file containing that section of the logfile from the final timestamp onwards.
I'm making a few assumptions. First, you don't want empty lines and second that any lines that contain two or more colons are timestamp lines. I haven't tested for the effects of "poison characters" (those symbols with a special meaning to cmd like !%^&><|)
If the procedure takes too long (such that it still has hold of the file when the generator program wants to perform the next write) then perhaps copying your log to a completely separate file then using this procedure on the copy may work for you.
if the file gets updated every 5 seconds and you want to show anything younger than 8 seconds, then you obviously want the last timestamp and following line(s):
#echo off
REM get LineNo of last TimeStamp (subtract one for future use)
for /f "delims=:" %%a in ('findstr /n /r "^[ 012][0-9]:[0-5][0-9]:[0-5][0-9]$" test.txt') do set /a LastStart=%%a-1
REM show, starting with the found LineNo
more +%LastStart% test.txt
(preserves any empty line(s), works even with "poison characters")

batch - execute command for every file with specific creation date

got this piece of code:
forfiles /P %ParentFolder% /S /M %Format% /C "cmd /c %exeFile% #path"
executing some exe for every file matching format as parameter.
any way to add "creation date" as a condition to run command via CMD?
something like :
for all files in directory (recursive) X if creation date newer then 1 day ago do (run) some exe with this file's path as param
This is not possible with forfiles, because, when the /D option is provided, it only regards the last modification date only (not even the modification time).
Unfortunately, there are no native commands for date/time maths, so I suggest to switch to a language that is capable of that; for instance, PowerShell, VBScript, JavaScript (which are all native to Windows past XP).
In case the modification date could be used, and a simple check with the date of today is sufficient, the following forfiles command line could be used:
forfiles /S /P "%ParentFolder%" /M "%Format%" /D +0 /C "cmd /C \"%exeFile%\" #path"
The /D option with a non-negative number lets forfiles return files that have been modified the given number of days after today or later (although you would need a time-machine; hence I consider this a design flaw). For +0 as the given number of days, all matching files modified today are returned, because forfiles /D only checks the modification date but does not care about the modification time.
If a simple equality check of the creation date with the date of today is fine for you, it can be done in batch-file scripting quite easily though (see all the explanatory rem remarks for how the following script works):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "ParentFolder=."
set "Format=*.*"
set "exeFile=" & rem // (full path to executable file)
set "tmpFile=%TEMP%\%~n0_%RANDOM%.tmp"
rem // Create a temporary file and retrieve its creation date:
2> nul del "%tmpFile%" & > "%tmpFile%" break & set "TODAY="
for /F "skip=5" %%J in ('dir /N /4 /-C /T:C "%tmpFile%"') do (
if not defined TODAY set "TODAY=%%J" & del "%tmpFile%"
)
rem // Change to predefined parent directory:
pushd "%ParentFolder%" && (
rem // Return all files recursively:
for /F "delims=" %%I in ('dir /B /S /A:-D "%Format%"') do (
rem // Determine the creation date for the current file:
set "FIRST=#"
for /F "skip=5" %%J in ('dir /N /-C /T:C "%%I"') do (
rem // Regard line listing file only, ignore summary lines:
if defined FIRST (
set "FIRST="
rem // Check creation date against today:
if "%%J"=="%TODAY%" (
rem // Return files created today:
echo "%%I" has been created today.
rem // Run external program on found file:
if defined exeFile "%exeFile%" "%%I"
)
)
)
)
rem // Restore previous working directory:
popd
)
endlocal
exit /B
I am using two dir command lines here:
the first one returns a bare list of files recursively (/S; no directories because of /A:-D) without any dates/times, headers and footers, due to switch /B; not using this switch would lead to header and footer lines for the whole output and for every iterated sub-directory also, so the output would be quite complicated to be parsed;
the second one receives each file returned by the first one; since there is no /B but the /N option, the file creation date/time is returned (/T:C); for every file the output looks like this:
Volume in drive D is DATA
Volume Serial Number is 0000-0000
Directory of D:\Data
2016/09/29 16:00 1024 current_file.txt
1 File(s) 1024 bytes
0 Dir(s) 1099511623680 bytes free
the first token of the sixth line constituting the creation date is split off and compared against the current date %DATE%; to ignore the header, the skip=5 option of the for /F loop is used; to ignore the summary lines, the variable FIRST is used;
Note that the date format is locale-dependent; as long as there appear no spaces in the date, this is no problem as the current date is also determined by the dir command applied on a temporary file.
Looking at the forfiles /? shows the switch /D that will exclude files that are younger (/d -<days>) or older (/d +<days>) than the given value for <days>.
As you want the files from today, you would set <date> to 0.
Notice, that this will look on the changed date!
Other way would be to use a for /r-loop and get a list of the creation date with dir /T:C ; to sort it add /O-D. Then separate that using a for /f loop to get the lines of the output of dir and another one to separate it (possibly easier without the nested loops).
You can than compare the creation date with %date% or when using one day=24 hrs compare the creation time with %time% additionally.

batch file to return next to last line of text file

I have a file that contains the output of a file compare thats written to a text file:
Comparing files C:\LOGS\old.txt and C:\LOGS\NEW.TXT
***** C:\LOGS\old.txt
***** C:\LOGS\NEW.TXT
folder_thats_different
*****
I need to pull out the next to last line "folder_thats_different" and put in a new string:
folder contains a file that is different: folder_thats_different
Yes, I know I can use another language, but I'm stuck with batch files for now.
You can try to read it with a for-loop and take the current line, and always save the previous line
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%x in (myFile.txt) do (
set "previous=!last!"
set "last=%%x"
)
echo !previous!
Here's an example you can use as a starting point. Just change the filename in the set command= line to the appropriate name (or replace the command with whatever will gerneate the log listing).
#echo off
#setlocal
(set command=type test.txt)
for /f "usebackq tokens=*" %%i in (`%command%`) do call :process_line %%i
echo next to last line: %old_line%
goto :eof
:process_line
(set old_line=%new_line%)
(set new_line=%*)
goto :eof
Of course, you'll probably want to do something other than simply echoing the found line.
The first answer works for me. I also added 2 lines after the end to allow it to repeat so I could watch an active log file without having to close and reopen it. I do a lot of debugging for the mods that are used in the game Space Engineers.
My version looks like this:
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%x in (SpaceEngineers.log) do (
set "previous=!last!"
set "last=%%x"
)
echo !previous!
timeout 15 /nobreak
se_log
The line below stops the batch file from looping too fast and stop the key bypass. To change the amount of time in seconds just change the number "15" to anything you want. To stop the batch file just press ctrl+c.
timeout 15 /nobreak
The line below is the name of the batch file I made so it will tell CMD to run this again.
se_log

Resources