Recursively fixing image file extensions in Windows 7 - windows

Hopefully this is a quick and easy question...
I have the same issue as this user: https://superuser.com/questions/812455/recursively-fixing-image-file-extensions-in-linux except I'm using a Windows (7) machine. (Most software ignores the extension like BlamKiwi mentions, but I'm frequently running into issues with Adobe software.)
I'm asking SO instead of SU since I just need some help translating the 'best answer' code into something that works on my machine.
for f in *.{jpg,JPG,png,PNG,jpeg,JPEG}; do
type=$( file "$f" | grep -oP '\w+(?= image data)' )
case $type in
PNG) newext=png ;;
JPEG) newext=jpg ;;
*) echo "??? what is this: $f"; continue ;;
esac
ext=${f##*.} # remove everything up to and including the last dot
if [[ $ext != $newext ]]; then
# remove "echo" if you're satisfied it's working
echo mv "$f" "${f%.*}.$newext"
fi
done
I'm guessing a .batch file can accomplish this but don't know enough to write it myself.

IrfanView is a freeware (for private usage) multimedia viewer which displays on viewing image files by default a message box if an image file has a file extension not suitable for image format prompting the user to rename the file for correcting the file extension. This message prompt can be disabled in configuration, but it is really useful to detect image files with wrong file extension on viewing them.
Images in Portable Network Graphics format always start with the 4 bytes 89 50 4E 47 (hexadecimal) which are displayed in a command prompt window on using code page 437 (North America) or code page 850 (Western Europe) as ëPNG.
Images in JPEG File Interchange Format start with the 10 bytes FF D8 FF E0 s1 s2 4A 46 49 46. The last 4 bytes of those 10 bytes are JFIF. s1 s2 are variable as being a length information.
The command FINDSTR is designed for searching for strings in text files, but can be used also for searching for strings in binary files. This is used by the batch file below:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "MessageOutput="
for /F "delims=" %%I in ('dir *.jpg *jpeg *.png /A-D /B /ON 2^>nul') do (
%SystemRoot%\System32\findstr.exe /M /C:PNG "%%I" >nul
if errorlevel 1 ( set "IsPNG=" ) else set "IsPNG=1"
%SystemRoot%\System32\findstr.exe /M /C:JFIF "%%I" >nul
if errorlevel 1 ( set "IsJPG=" ) else set "IsJPG=1"
if defined IsPNG if defined IsJPG (
set "FirstPNG=1"
for /F "delims=:" %%J in ('%SystemRoot%\System32\findstr.exe /N /C:PNG "%%I"') do (
if defined FirstPNG (
if "%%J" == "1" ( set "IsJPG=" ) else set "IsPNG="
set "FirstPNG="
)
)
)
if defined IsPNG (
if /I not "%%~xI" == ".png" (
echo PNG image "%%I" has wrong file extension.
ren "%%I" "%%~nI.png"
set "MessageOutput=1"
)
) else if defined IsJPG (
if /I "%%~xI" == ".png" (
echo JPG image "%%I" has wrong file extension.
ren "%%I" "%%~nI.jpg"
set "MessageOutput=1"
)
) else (
echo/
echo ERROR: File "%%I" is neither a JPG nor a PNG file.
echo/
set "MessageOutput=1"
)
)
if defined MessageOutput (
echo/
pause
)
endlocal
The command DIR is executed by command FOR in a separate command process in background to get a list of *.jpg, *.jpeg and *.png file names without path of files in current directory on execution of the batch file.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
It is necessary here to use command DIR instead of using
for %%I in (*.jpg *jpeg *.png) do (
because the list output by DIR, captured and processed by FOR line by line does not change anymore during the loop iterations. This is important here because of perhaps changing the list of file entries in current directory due to a rename operation. By using FOR directly it could happen after a file rename that a file is processed twice or another file in directory is skipped because of directory list change during loop iteration.
The code is written to avoid usage of delayed environment variable expansion to work also for files with an exclamation mark. A different solution would be also possible with using a subroutine.
For each file found by DIR first a case-sensitive search for PNG is executed on the file and environment variable IsPNG is deleted on not found in file or is defined with value 1 on file containing PNG.
Next a case-sensitive search for JFIF is executed on the file and environment variable IsJPG is deleted on not found in file or is defined with value 1 on file containing JFIF.
Binary data of an image file can contain anywhere in the data by chance also the hexadecimal byte sequence 50 4E 47 or 4A 46 49 46. The third IF command line checks if the file contains both byte sequences. In this rare case FINDSTR searching for PNG is executed once again with evaluating in which "line" the string is found. If the line number is 1, then the string PNG is definitely at top of the file which is a quite clear indication that the file is a PNG file, otherwise it should be a JPEG file containing by chance also the bytes 50 4E 47. The worst case handled by the batch code is a PNG image file containing PNG in header and in data PNG and JFIF which requires evaluating only first occurrence of PNG found by FINDSTR.
An appropriate message is output and file is renamed (without checking for success) if current image file is (most likely) a PNG file, but does not have expected file extension .png. For a *.png file being (most likely) a JPEG file a similar message is output and the file rename is executed for changing the file extension.
It is also possible that the file with extension .png, .jpg or .jpeg does neither contain PNG nor JFIF, for example on being a GIF file. In this case an error message is output as the image format could not be determined by this batch code.
The batch file execution is halted at end in case of any message is output. This means on double clicking the batch file a console window appears and closes if all *.jpg, *.jpeg and *.png file have the right file extension according to image format.
Of course this solution is definitely not perfect. A text file with first line containing PNG and second line containing JFIF and having the file extension .jpg would interpreted as PNG image file although being in real a text file. IrfanView is definitely a better choice for really checking if a *.jpg, *.jpeg or *.png file is really a JPEG or PNG image file.
Here is additionally an alternate solution using a subroutine:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "MessageOutput="
for /F "delims=" %%I in ('dir *.jpg *jpeg *.png /A-D /B /ON 2^>nul') do call :CheckImage "%%I"
if defined MessageOutput (
echo/
pause
)
endlocal
goto :EOF
:CheckImage
set "IsJPG=0"
set "IsPNG=0"
%SystemRoot%\System32\findstr.exe /M /C:PNG %1 >nul
if not errorlevel 1 set "IsPNG=1"
%SystemRoot%\System32\findstr.exe /M /C:JFIF %1 >nul
if not errorlevel 1 set "IsJPG=1"
if %IsPNG% == 1 if %IsJPG% == 1 (
for /F "delims=:" %%J in ('%SystemRoot%\System32\findstr.exe /N /C:PNG %1') do (
if "%%J" == "1" ( set "IsJPG=0" ) else set "IsPNG=0"
goto CheckExtension
)
)
:CheckExtension
if %IsPNG% == 1 (
if /I not "%~x1" == ".png" (
echo PNG image %1 has wrong file extension.
ren %1 "%~n1.png"
set "MessageOutput=1"
)
goto :EOF
)
if %IsJPG% == 1 (
if /I "%~x1" == ".png" (
echo JPG image %1 has wrong file extension.
ren %1 "%~n1.jpg"
set "MessageOutput=1"
)
goto :EOF
)
echo/
echo ERROR: File %1 is neither a JPG nor a PNG file.
echo/
set "MessageOutput=1"
goto :EOF
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
findstr /?
for /?
if /?
pause /?
ren /?
set /?
setlocal /?

Related

Extract 7-zip files into their containing directory from a batch file?

I have a directory, c:\7zip\test, containing many subdirectories, some containing 7-zip files.
I want to find and extract the 7-zip files into their holding directories from a batch file and then delete those 7-zip files.
#If "%1"=="" (Set pathf=c:\7zip\test\) else (Set pathf=%1)
#If "%2"=="" (Set lzma2_test=*.7z) else (Set lzma2_test=%2)
for /r "%pathf%" %%f in ("%lzma2_test%") do 7z x -y -mx1 -m0=lzma2 "%%f" -oFolderName
del "%pathf%" + subfolder name (if needed) + 7-zip file name
Instead of FolderName, I should send a directory name, but I do not know how. And the same for del.
Open a command prompt, run for /? and read the output help from top of first page to bottom of last page, especially the section about referencing the loop variable with a modifier like %~dpI with I being the loop variable to reference the drive and path.
#echo off
if "%~1" == "" (set "pathf=c:\7zip\test") else (set "pathf=%~1")
if "%~2" == "" (set "lzma2_test=*.7z") else (set "lzma2_test=%~2")
for /R "%pathf%" %%I in ("%lzma2_test%") do (
7z.exe x -y -o"%%~dpI" "%%I"
if not errorlevel 1 del "%%I"
)
The compression switches -mx1 -m0=lzma2 are useless on extracting an archive. The archive contains in header the algorithm used on compression and so extraction works always as long as used 7z.exe supports the archive type and used algorithm on creating the archive.
The archive file is only deleted if the extraction was successful, i.e. 7x.exe exited not with a value greater or equal 1 which means equal 0 which is the exit code for a successful extraction.
It would be more safe to use the following code which works also if an archive file contains inside another archive file. This code is recommended also for usage on a FAT16, FAT32 or exFAT drive.
#echo off
if "%~1" == "" (set "pathf=c:\7zip\test") else (set "pathf=%~1")
if "%~2" == "" (set "lzma2_test=*.7z") else (set "lzma2_test=%~2")
for /F "eol=| delims=" %%I in ('dir "%pathf%\%lzma2_test%" /A-D /B /S 2^>nul') do (
7z.exe x -y -o"%%~dpI" "%%I"
if not errorlevel 1 del "%%I"
)
This code makes sure not extracting an archive file inside an archive file by chance which as it could happen by the first code which iterates of the list of archive files with the list changing on each iteration on extracting an archive file containing an archive file and because of deletion of the successfully extracted archives.
It would be a good idea to test the used batch file first by running it from within a command prompt window with echo left to 7z.exe and if not errorlevel 1 to see what would be executed without really executing it.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
dir /?
echo /?
for /?
if /?
set /?
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background with %ComSpec% /c and the command line between ' appended as additional arguments.

How to extract certain portion of a file that starts with "HDR" followed by a search keyword?

How to extract portion of file that starts with HDR followed by search keyword using a batch file and Windows command interpreter?
Only certain HDR should be copied to another file with name GoodHDR.txt.
HDRs not included in searches should be copied also to another file with name BadHDR.txt.
For example, I have HeaderList.txt below and need to get HEADER0001 and HEADER0003 portions.
HDRHEADER0001 X004010850P
BEG00SAD202659801032017021699CANE
HDRHEADER0002 X004010850P
BEG00SAD202611701012017021499CANW
DTM01020170214
N1ST 92 0642397236
N315829 RUE BELLERIVE
N4MONTREAL QCH1A5A6 CANADA
HDRHEADER0003 X004010850P
BEG00SAP521006901012017021399CANOUT B16885
DTM01020170213
N1STCEGEP SAINT LAURENT 92 0642385892
Expected outcome:
GoodHDR.txt only contains HEADER0001 and HEADER0003.
HDRHEADER0001 X004010850P
BEG00SAD202659801032017021699CANE
HDRHEADER0003 X004010850P
BEG00SAP521006901012017021399CANOUT B16885
DTM01020170213
N1STCEGEP SAINT LAURENT 92 0642385892
BadHDR.txt contains HEADER0002:
HDRHEADER0002 X004010850P
BEG00SAD202611701012017021499CANW
DTM01020170214
N1ST 92 0642397236
N315829 RUE BELLERIVE
N4MONTREAL
The batch code below expects to be started with the parameters 0001 0003 to produce the two output files from source file as posted in question.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceFile=HeaderList.txt"
set "FoundFile=GoodHDR.txt"
set "IgnoreFile=BadHDR.txt"
if "%~1" == "" goto ShowHelp
if "%~1" == "/?" goto ShowHelp
if not exist "%SourceFile%" goto NoHeaderList
del "%IgnoreFile%" 2>nul
del "%FoundFile%" 2>nul
rem Assign the headers passed as arguments to environment variables with
rem name HDR%~1X, HDR%~2X, HDR%~3X, etc. used later for quickly searching
rem for number of current header within the list of specified numbers.
rem All parameter strings not existing of exactly 4 digits are ignored.
set HeadersCount=0
:SetHeaders
set "HeaderNumber=%~1"
if "%HeaderNumber:~3,1%" == "" goto NextArgument
if not "%HeaderNumber:~4,1%" == "" goto NextArgument
for /F "delims=0123456789" %%I in ("%HeaderNumber%") do goto NextArgument
set "HDR%HeaderNumber%X=%HeaderNumber%"
set /A HeadersCount+=1
:NextArgument
shift /1
if not "%~1" == "" goto SetHeaders
if %HeadersCount% == 0 goto ShowHelp
rem Proces the header blocks in the source file.
set "OutputFile=%IgnoreFile%"
for /F "usebackq delims=" %%L in ("%SourceFile%") do call :ProcessLine "%%L"
rem Output a summary information of header block separation process.
if "%HeadersCount%" == "-1" set "HeadersCount="
if not defined HeadersCount (
echo All header blocks found and written to file "%FoundFile%".
goto EndBatch
)
set "SingularPlural= was"
if not %HeadersCount% == 1 set "SingularPlural=s were"
echo Following header block%SingularPlural% not found:
echo/
for /F "tokens=2 delims==" %%V in ('set HDR') do echo %%V
goto EndBatch
rem ProcessLine is a subroutine called from main FOR loop with
rem a line read from source file as first and only parameter.
rem It compares the beginning of the line with HDRHEADER. The line is
rem written to active output file if it does not start with that string.
rem Otherwise the string after HDRHEADER is extracted from the
rem line and searched in list of HDR environment variables.
rem Is the header in list of environment variables, this line and all
rem following lines up to next header line or end of source file are
rem written to file with found header blocks.
rem Otherwise the current header line and all following lines up to
rem next header line or end of source file are written to file with
rem header blocks to ignore.
rem Once all header blocks to find are indeed found and written completely
rem to the file for found header blocks, all remaining lines of source file
rem are written to the ignore file without further evaluation.
:ProcessLine
if not defined HeadersCount (
>>"%OutputFile%" echo %~1
goto :EOF
)
set "Line=%~1"
if not "%Line:~0,9%" == "HDRHEADER" (
>>"%OutputFile%" echo %~1
goto :EOF
)
set "HeaderLine=%Line:~9%"
for /F %%N in ("%HeaderLine%") do set "HeaderNumber=%%N"
set "OutputFile=%IgnoreFile%"
for /F %%N in ('set HDR%HeaderNumber%X 2^>nul') do (
set "HDR%HeaderNumber%X="
set /A HeadersCount-=1
set "OutputFile=%FoundFile%"
)
>>"%OutputFile%" echo %~1
if %HeadersCount% == 0 (
set "HeadersCount=-1"
) else if %HeadersCount% == -1 (
set "HeadersCount="
)
goto :EOF
:NoHeaderList
echo Error: The file "%SourceFile%" could not be not found in directory:
echo/
echo %CD%
goto EndBatch
:ShowHelp
echo Searches for specified headers in "%SourceFile%" and writes the
echo found header blocks to file "%FoundFile%" and all other to file
echo "%IgnoreFile%" and outputs the header blocks not found in file.
echo/
echo %~n0 XXXX [YYYY] [ZZZZ] [...]
echo/
echo %~nx0 must be called with at least one header number.
echo Only numbers with 4 digits are accepted as parameters.
:EndBatch
echo/
endlocal
pause
The redirection operator >> and the current name of the output file is specified at beginning of all lines which print with command ECHO the current line to avoid appending a trailing space on each line written to an output file and get the line printing nevertheless working if a line ends with 1, 2, 3, ...
Some additional notes about limitations on usage of this code:
The batch code is written with avoiding the usage of delayed expansion to be able to easily process also lines containing an exclamation mark. The disadvantage of not using delayed expansion is that lines containing characters in a line with a special meaning on command line like &, >, <, |, etc. result in wrong output and can even produce additional, unwanted files in current directory.It would be of course possible to extend the batch code to work also for lines in source file containing any ANSI character, but this is not necessary according to source file example which does not contain any "poison" character.
FOR ignores empty lines on reading lines from a text file. So the code as is produces 1 or 2 output files with no empty lines copied from source file.
The main FOR loop reading the lines from source file skips all lines starting with a semicolon. If this could be a problem, specify on FOR command line reading the lines from source file before delims= the parameter eol= with a character which definitely never exists at beginning of a line in source file. See help of command FOR displayed on running in a command prompt window for /? for details on parameters of set /F like eol=, delims= and tokens=.
The length of a string assigned to an environment variable plus equal sign plus name of environment variable is limited to 8192 characters. For that reason this batch code can't be used for a source file with lines longer than 8187 characters.
The length of a command line is also limited. The maximum length depends on version of Windows. So this batch file can't be used with a very large number of header numbers.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
del /?
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
shift /?
Read also the Microsoft article about Using Command Redirection Operators for details about >> and 2>nul and 2^>nul with redirection operator > being escaped with caret character ^ for being interpreted as literal character on parsing FOR command line, but as redirection operator later on execution of command SET by command FOR.

Loop through files in a folder and check if they have different extensions

I have a folder that contains files; each document should have .pdf and .xml format. I need to write a BAT file to run from a scheduled task to verify that both documents exist for each.
My logic is:
loop through files in the folder
strip each file to its name without extension
check that same name files exist for both .xml and pdf.
if not mark a flag variable as problem
when done, if the flag variable is marked, send an Email notification
I know how to use blat to sending email, but I'm having trouble to execute the loop. I found a way to get path and file name without extension but can't merge them.
I've used batch files a few time, before but I'm far from an expert. What am I missing?
Here's the code I have so far:
set "FolderPath=E:\TestBat\Test\"
echo %FolderPath%
for %%f in (%FolderPath%*) do (
set /p val=<%%f
For %%A in ("%%f") do (
Set Folder=%%~dpA
Set Name=%%~nxA
)
echo Folder is: %Folder%
echo Name is: %Name%
if NOT EXIST %FolderPath%%name%.xml
set flag=MISSING
if NOT EXIST %FolderPath%%name%.pdf
set flag=MISSING
)
echo %Flag%
pause
There is no need for fancy code for a task such as this:
#Echo Off
Set "FolderPath=E:\TestBat\Test"
If /I Not "%CD%"=="%FolderPath%" PushD "%FolderPath%" 2>Nul||Exit/B
Set "flag="
For %%A In (*.pdf *.xml) Do (
If /I "%%~xA"==".pdf" (If Not Exist "%%~nA.xml" Set "flag=MISSING")
If /I "%%~xA"==".xml" (If Not Exist "%%~nA.pdf" Set "flag=MISSING")
)
If Defined flag Echo=%flag%
Timeout -1
Something like this :
set "FolderPath=E:\TestBat\Test\"
pushd "%FolderPath%"
for %%a in (*.xml) do (
if exist "%%~na.pdf"(
echo ok
) else (
rem do what you want here
echo Missing
)
)
popd
Is this what you want?
#echo off
setlocal enabledelayedexpansion
set "FolderPath=E:\TestBat\Test\"
echo !FolderPath!
for /f "usebackq delims=" %%f in (`dir !FolderPath! /B`) do (
set /p val=<%%f
For %%A in ("%%f") do (
Set Folder=%%~dpA
Set name=%%~nxA
)
echo Folder is: !Folder!
echo Name is: !name!
if NOT EXIST !FolderPath!!name!.xml set flag=MISSING
if NOT EXIST !FolderPath!!name!.pdf set flag=MISSING
)
echo Flag: !flag!
pause
endlocal
You should reformat your code and keep in mind that the grama for batch file is critical. BTW, if you are trying to update the existing batch variable and read it later, you should enable localdelayedexpansion and use ! instead of %.
Keep it simple:
#echo off
pushd "E:\TestBat\Test" || exit /B 1
for %%F in ("*.pdf") do if not exist "%%~nF.xml" echo %%~nxF
for %%F in ("*.xml") do if not exist "%%~nF.pdf" echo %%~nxF
popd
This returns all files that appear orphaned, that is, where the file with the same name but the other extension (.pdf, .xml) is missing. To implement a variable FLAG to indicate there are missing files, simply append & set "FLAG=missing" to each for line and ensure FLAG is empty initially. Then you can check it later by simply using if defined FLAG.
Note: This does not cover the e-mail notification issue. Since I do not know the BLAT tool you mentioned, I have no clue how you want to transfer the listed files to it (command line arguments, temporary file, or STDIN stream?).
In case there is a huge number of files in the target directory, another approach might be better in terms of performance, provided that the number of file system accesses is reduced drastically (note that the above script accesses the file system within the for loop body by if exist, hence for every iterated file individually). So here is an attempt relying on a temporary file and the findstr command:
#echo off
pushd "E:\TestBat\Test" || exit /B 1
rem // Return all orphaned `.pdf` files:
call :SUB "*.pdf" "*.xml"
rem // Return all orphaned `.xml` files:
call :SUB "*.xml" "*.pdf"
popd
exit /B
:SUB val_pattern_orphaned val_pattern_missing
set "LIST=%TEMP%\%~n0_%RANDOM%.tmp"
> "%LIST%" (
rem // Retrieve list of files with one extension:
for %%F in ("%~2") do (
rem /* Replace the extension by the other one,
rem then write the list to a temporary file;
rem this constitutes a list of expected files: */
echo(%%~nF%~x1
)
)
rem /* Search actual list of files with the other extension
rem for occurrences of the list of expected files and
rem return each item that does not match: */
dir /B /A:-D "%~1" | findstr /L /I /X /V /G:"%LIST%"
rem // Clean up the temporary file:
del "%LIST%"
exit /B
To understand how it works, let us concentrate on the first sub-routine call call :SUB "*.pdf" "*.xml" using an example; let us assume the target directory contains the following files:
AlOnE.xml
ExtrA.pdf
sAmplE.pdf
sAmplE.xml
So in the for loop a list of .xml files is gathered:
AlOnE.xml
sAmplE.xml
This is written to a temporary file but with the extensions .xml replaced by .pdf:
AlOnE.pdf
sAmplE.pdf
The next step is to generate a list of actually existing .pdf files:
ExtrA.pdf
sAmplE.pdf
This is piped into a findstr command line, that searches this list for search strings that are gathered from the temporary file, returning non-matching lines only. In other words, findstr returns only those lines of the input list that do not occur in the temporary file:
ExtrA.pdf
To finally get also orphaned .xml files, the second sub-routine call is needed.
Since this script uses a temporary file containing a file list which is processed once by findstr to find any orphaned files per extension, the overall number of file system access operations is lower. The weakest part however is the for loop (containing string concatenation operations).

How to get attributes of a file using batch file

I am trying to make a batch file to delete malicious files from pendrive. I know that these malicious files uses hidden,read only and system attributes mainly to hide itself from users. Currently i am deleting these files using cmd by removing malicious files attributes then deleting it. Now I am thinking to make a small batch file which can be used to remove these files just by entering the drive letter.
I have found this code in a website to find attributes of a file. But after entering the name of the file the batch file just exits without showing any results.
#echo off
setlocal enabledelayedexpansion
color 0a
title Find Attributes in Files
:start
set /p atname=Name of the file:
if not exist %atname% (
cls
echo No file of that name exists!
echo.
echo Press any key to go back
pause>nul
goto start
)
for /f %%i in (%atname%) do set attribs=%%~ai
set attrib1=!attribs:~0,1!
set attrib2=!attribs:~1,1!
set attrib3=!attribs:~2,1!
set attrib4=!attribs:~3,1!
set attrib5=!attribs:~4,1!
set attrib6=!attribs:~5,1!
set attrib7=!attribs:~6,1!
set attrib8=!attribs:~7,1!
set attrib9=!attribs:~8,1!
cls
if %attrib1% equ d echo Directory
if %attrib2% equ r echo Read Only
if %attrib3% equ a echo Archived
if %attrib4% equ h echo Hidden
if %attrib5% equ s echo System File
if %attrib6% equ c echo Compressed File
if %attrib7% equ o echo Offline File
if %attrib8% equ t echo Temporary File
if %attrib9% equ l echo Reparse point
echo.
echo.
echo Press any key to go back
pause>nul
goto start
can you tell me why this batch file is exiting without showing any results. Or can you give any better batch script for getting attributes of a file.
EDIT
I was able to work the above code only for a single file. As my purpose of my batch file is to remove malicious files by entering the drive letter. How can i use it to find what kind of attributes files are using in a particular drive.
For example:
In cmd we can use this command to find the file attributes of a given drive
attrib *.*
Advance thanks for your help
I tried the bat file (without inspecting the details) and it seems to work fine for me. What I noticed is that it closes instantly if you don't enclose file path with quotation marks - e.g. "file". Example:
Name of the file: path\file.txt // this will close immediately
Name of the file: "path\file.txt" // now it will stay open and display the result
This hopefully solves your problem.
As far as your question in EDIT is concerned, a simple option is to iterate a list of files and execute the batch on each one.
batch1.bat: (%1 refers to the first command-line parameter)
#echo off
setlocal enabledelayedexpansion
echo %1
set atname=%1
for %%i in ("%atname%") do set attribs=%%~ai
set attrib1=!attribs:~0,1!
set attrib2=!attribs:~1,1!
set attrib3=!attribs:~2,1!
set attrib4=!attribs:~3,1!
set attrib5=!attribs:~4,1!
set attrib6=!attribs:~5,1!
set attrib7=!attribs:~6,1!
set attrib8=!attribs:~7,1!
set attrib9=!attribs:~8,1!
cls
if %attrib1% equ d echo Directory
if %attrib2% equ r echo Read Only
if %attrib3% equ a echo Archived
if %attrib4% equ h echo Hidden
if %attrib5% equ s echo System File
if %attrib6% equ c echo Compressed File
if %attrib7% equ o echo Offline File
if %attrib8% equ t echo Temporary File
if %attrib9% equ l echo Reparse point
echo.
echo.
Next, generate a list of all files within a given path (say 'folder' including all subfolders):
dir /s /b folder > ListOfFiles.txt
main.bat (read ListOfFiles.txt line-by-line and pass each line to batch1.bat as a command line parameter):
#echo off
for /f "tokens=*" %%l in (ListOfFiles.txt) do (batch1.bat %%l)
Then, from cmd:
main.bat >> output.txt
The last step generates an output file with complete results. Granted, this can be done in a more polished (and probably shorter) way, but that's one obvious direction you could take.
You're using a for /f loop here, which isn't necessary (and may yield undesired results if the filename contains spaces). Change this:
for /f %%i in (%atname%) do set attribs=%%~ai
into this:
for %%i in ("%atname%") do set attribs=%%~ai
This is dangerous code - but it'll delete read only, hidden and system files.
It should fail to run on c: drive but I haven't tested it. Note that some Windows installs are on drives other than c:
#echo off
echo "%cd%"|find /i "c:\" >nul || (
del *.??? /ar /s /f
del *.??? /ah /s
del *.??? /as /s
)

Drag and drop batch file for multiple files?

I wrote a batch file to use PngCrush to optimize a .png image when I drag and drop it onto the batch file.
In the what's next section, I wrote about what I thought would be a good upgrade to the batch file.
My question is: is it possible to create a batch file like I did in the post, but capable of optimizing multiple images at once? Drag and drop multiple .png files on it? (and have the output be something like new.png, new(1).png, new(2).png, etc...
Yes, of course this is possible. When dragging multiple files on a batch file you get the list of dropped files as a space-separated list. You can verify this with the simple following batch:
#echo %*
#pause
Now you have two options:
PngCrush can already handle multiple file names given to it on the command line. In this case all you'd have to do would be to pass %* to PngCrush instead of just %1 (as you probably do now):
#pngcrush %*
%* contains all arguments to the batch file, so this is a convenient way to pass all arguments to another program. Careful with files named like PngCrush options, though. UNIX geeks will know that problem :-)
After reading your post describing your technique, however, this won't work properly as you are writing the compressed file to new.png. A bad idea if you're handling multiple files at once as there can be only one new.png :-). But I just tried out that PngCrush handles multiple files just well, so if you don't mind an in-place update of the files then putting
#pngcrush -reduce -brute %*
into your batch will do the job (following your original article).
PngCrush will not handle multiple files or you want to write each image to a new file after compression. In this case you stick with your "one file at a time" routine but you loop over the input arguments. In this case, it's easiest to just build a little loop and shift the arguments each time you process one:
#echo off
if [%1]==[] goto :eof
:loop
pngcrush -reduce -brute %1 "%~dpn1_new%~x1"
shift
if not [%1]==[] goto loop
What we're doing here is simple: First we skip the entire batch if it is run without arguments, then we define a label to jump to: loop. Inside we simply run PngCrush on the first argument, giving the compressed file a new name. You may want to read up on the path dissection syntax I used here in help call. Basically what I'm doing here is name the file exactly as before; I just stick "_new" to the end of the file name (before the extension). %~dpn1 expands to drive, path and file name (without extension), while %~x1 expands to the extension, including the dot.
ETA: Eep, I just read your desired output with new.png, new(1).png, etc. In this case we don't need any fancy path dissections but we have other problems to care about.
The easiest way would probably be to just start a counter at 0 before we process the first file and increment it each time we process another one:
#echo off
if [%1]==[] goto :eof
set n=0
:loop
if %n%==0 (
pngcrush -reduce -brute %1 new.png
) else (
pngcrush -reduce -brute %1 new^(%n%^).png
)
shift
set /a n+=1
if not [%1]==[] goto loop
%n% is our counter here and we handle the case where n is 0 by writing the result to new.png, instead of new(0).png.
This approach has problems, though. If there are already files named new.png or new(x).png then you will probably clobber them. Not nice. So we have to do something different and check whether we can actually use the file names:
rem check for new.png
if exist new.png (set n=1) else (set n=0 & goto loop)
rem check for numbered new(x).png
:checkloop
if not exist new^(%n%^).png goto loop
set /a n+=1
goto checkloop
The rest of the program stays the same, including the normal loop. But now we start at the first unused file name and avoid overwriting files that are already there.
Feel free to adapt as needed.
To do Drag & Drop in a secure way, isn't so simple with batch.
Dealing with %1, shift or %* could fail, because the explorer is not very smart, while quoting the filenames, only filenames with spaces are quoted.
But files like Cool&stuff.png are not quoted by the explorer so you get a cmdline like
pngCr.bat Cool&stuff.png
So in %1 is only Cool even in %* is only Cool, but after the batch ends, cmd.exe tries to execute a stuff.png (and will fail).
To handle this you could access the parameters with !cmdcmdline! instead of %1 .. %n,
and to bypass a potential error at the end of execution, a simple exit could help.
#echo off
setlocal ENABLEDELAYEDEXPANSION
rem Take the cmd-line, remove all until the first parameter
set "params=!cmdcmdline:~0,-1!"
set "params=!params:*" =!"
set count=0
rem Split the parameters on spaces but respect the quotes
for %%G IN (!params!) do (
set /a count+=1
set "item_!count!=%%~G"
rem echo !count! %%~G
)
rem list the parameters
for /L %%n in (1,1,!count!) DO (
echo %%n #!item_%%n!#
)
pause
REM ** The exit is important, so the cmd.ex doesn't try to execute commands after ampersands
exit
Btw. there is a line limit for drag&drop operations of ~2048 characters, in spite of the "standard" batch line limit of ~8192 characters.
As for each file the complete path is passed, this limit can be reached with few files.
FOR %%A IN (%*) DO (
REM Now your batch file handles %%A instead of %1
REM No need to use SHIFT anymore.
ECHO %%A
)
And to differentiate between dropped files and folders, you can use this:
FOR %%I IN (%*) DO (
ECHO.%%~aI | FIND "d" >NUL
IF ERRORLEVEL 1 (
REM Processing Dropped Files
CALL :_jobF "%%~fI"
) ELSE (
REM Processing Dropped Folders
CALL :_jobD "%%~fI"
)
)
This is a very late answer, Actually I was not aware of this old question and prepared an answer for this similar one where there was a discussion about handling file names with special characters because explorer only quotes file names that contain space(s). Then in the comments on that question I saw a reference to this thread, after that and not to my sureprise I realized that jeb have already covered and explained this matter very well, which is expected of him.
So without any further explanations I will contribute my solution with the main focus to cover more special cases in file names with this ,;!^ characters and also to provide a mechanism to guess if the batch file is directly launched by explorer or not, so the old fashion logic for handling batch file arguments could be used in all cases.
#echo off
setlocal DisableDelayedExpansion
if "%~1" EQU "/DontCheckDrapDrop" (
shift
) else (
call :IsDragDrop && (
call "%~f0" /DontCheckDrapDrop %%#*%%
exit
)
)
:: Process batch file arguments as you normally do
setlocal EnableDelayedExpansion
echo cmdcmdline=!cmdcmdline!
endlocal
echo,
echo %%*=%*
echo,
if defined #* echo #*=%#*%
echo,
echo %%1="%~1"
echo %%2="%~2"
echo %%3="%~3"
echo %%4="%~4"
echo %%5="%~5"
echo %%6="%~6"
echo %%7="%~7"
echo %%8="%~8"
echo %%9="%~9"
pause
exit /b
:: IsDragDrop routine
:: Checks if the batch file is directly lanched through Windows Explorer
:: then Processes batch file arguments which are passed by Drag'n'Drop,
:: rebuilds a safe variant of the arguments list suitable to be passed and processed
:: in a batch script and returns the processed args in the environment variable
:: that is specified by the caller or uses #* as default variable if non is specified.
:: ErrorLevel: 0 - If launched through explorer. 1 - Otherwise (Will not parse arguments)
:IsDragDrop [retVar=#*]
setlocal
set "Esc="
set "ParentDelayIsOff=!"
setlocal DisableDelayedExpansion
if "%~1"=="" (set "ret=#*") else set "ret=%~1"
set "Args="
set "qsub=?"
:: Used for emphasis purposes
set "SPACE= "
setlocal EnableDelayedExpansion
set "cmdline=!cmdcmdline!"
set ^"ExplorerCheck=!cmdline:%SystemRoot%\system32\cmd.exe /c ^""%~f0"=!^"
if "!cmdline!"=="!ExplorerCheck!" (
set ^"ExplorerCheck=!cmdline:"%SystemRoot%\system32\cmd.exe" /c ^""%~f0"=!^"
if "!cmdline!"=="!ExplorerCheck!" exit /b 1
)
set "ExplorerCheck="
set ^"cmdline=!cmdline:*"%~f0"=!^"
set "cmdline=!cmdline:~0,-1!"
if defined cmdline (
if not defined ParentDelayIsOff (
if "!cmdline!" NEQ "!cmdline:*!=!" set "Esc=1"
)
set ^"cmdline=!cmdline:"=%qsub%!"
)
(
endlocal & set "Esc=%Esc%"
for /F "tokens=*" %%A in ("%SPACE% %cmdline%") do (
set "cmdline=%%A"
)
)
if not defined cmdline endlocal & endlocal & set "%ret%=" & exit /b 0
:IsDragDrop.ParseArgs
if "%cmdline:~0,1%"=="%qsub%" (set "dlm=%qsub%") else set "dlm= "
:: Using '%%?' as FOR /F variable to not mess with the file names that contain '%'
for /F "delims=%dlm%" %%? in ("%cmdline%") do (
set ^"Args=%Args% "%%?"^"
setlocal EnableDelayedExpansion
set "cmdline=!cmdline:*%dlm: =%%%?%dlm: =%=!"
)
(
endlocal
for /F "tokens=*" %%A in ("%SPACE% %cmdline%") do (
set "cmdline=%%A"
)
)
if defined cmdline goto :IsDragDrop.ParseArgs
if defined Esc (
set ^"Args=%Args:^=^^%^"
)
if defined Esc (
set ^"Args=%Args:!=^!%^"
)
(
endlocal & endlocal
set ^"%ret%=%Args%^"
exit /b 0
)
OUTPUT with sample files dragged and dropped onto the batch file:
cmdcmdline=C:\Windows\system32\cmd.exe /c ""Q:\DragDrop\DragDrop.cmd" Q:\DragDrop\ab.txt "Q:\DragDrop\c d.txt" Q:\DragDrop\!ab!c.txt "Q:\DragDrop\a b.txt" Q:\DragDrop\a!b.txt Q:\DragDrop\a&b.txt Q:\DragDrop\a(b&^)).txt Q:\DragDrop\a,b;c!d&e^f!!.txt Q:\DragDrop\a;b.txt"
%*=/DontCheckDrapDrop "Q:\DragDrop\ab.txt" "Q:\DragDrop\c d.txt" "Q:\DragDrop\!ab!c.txt" "Q:\DragDrop\a b.txt" "Q:\DragDrop\a!b.txt" "Q:\DragDrop\a&b.txt" "Q:\DragDrop\a(b&^)).txt" "Q:\DragDrop\a,b;c!d&e^f!!.txt" "Q:\DragDrop\a;b.txt"
#*= "Q:\DragDrop\ab.txt" "Q:\DragDrop\c d.txt" "Q:\DragDrop\!ab!c.txt" "Q:\DragDrop\a b.txt" "Q:\DragDrop\a!b.txt" "Q:\DragDrop\a&b.txt" "Q:\DragDrop\a(b&^)).txt" "Q:\DragDrop\a,b;c!d&e^f!!.txt" "Q:\DragDrop\a;b.txt"
%1="Q:\DragDrop\ab.txt"
%2="Q:\DragDrop\c d.txt"
%3="Q:\DragDrop\!ab!c.txt"
%4="Q:\DragDrop\a b.txt"
%5="Q:\DragDrop\a!b.txt"
%6="Q:\DragDrop\a&b.txt"
%7="Q:\DragDrop\a(b&^)).txt"
%8="Q:\DragDrop\a,b;c!d&e^f!!.txt"
%9="Q:\DragDrop\a;b.txt"
In :IsDragDrop routine I specially tried to minimize the assumptions about command line format and spacing between the arguments. The detection (guess) for explorer launch is based on this command line signature %SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments"
So it is very possible to fool the code into thinking it has launched by double click from explorer or by drag'n'drop and that's not an issue and the batch file will function normally.
But with this particular signature it is not possible to intentionally launch batch file this way: %SystemRoot%\system32\cmd.exe /c ""FullPathToBatchFile" Arguments & SomeOtherCommand" and expect that the SomeOtherCommand to be executed, instead it will be merged into the batch file arguments.
You don't need a batch script to optimize multiple PNGs, all you need is the wildcard:
pngcrush -d "crushed" *.png
That will pngcrush all PNGs in the current dir and move them to a sub-dir named "crushed". I would add the -brute flag to likely shave off a few more bytes.
pngcrush -d "crushed" -brute *.png
I'm posting this because it doesn't seem to be well documented or widely known, and because it may be easier for you in the long run than writing and maintaining a drag and drop batch file.

Resources