简体   繁体   中英

Recursively fixing image file extensions in Windows 7

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 /?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM