Shell script to identify .blend file version

UPDATED for 5.0+

“How do I know what version of Blender created the .blend file?”

It seems that this question has been asked a lot, and the general answer is to open the .blend in Notepad and look at the file header. But…

  • that’s clunky
  • and it doesn’t work for compressed .blends

I often work from the terminal prompt anyway — and the information about the file version is something I tend to want to know before I’ve even launched Blender itself. So…

This is a python script to do it from the console / terminal prompt.


REQUIREMENTS


  • Python3 installed and listed in your command PATH.
    • For Windows users: just use the Python Install Manager
    • For Linux users, assuming which python3 prints nothing, use a distro-appropriate command like sudo apt install python3 (for Ubuntu & Debian, at least).
    • For OS X users, brew install python. I won’t walk you through installing Homebrew myself if you don’t already have it. Here’s another guide to install both Homebrew and Python.
    • For the brave who know how to do it, you could actually just use Blender’s python installation by adding it to the PATH. Read more below under “Where To Put The Script”.

  • pip (Package Installer for Python) — this comes with Python by default, but if your python for some odd reason does not have it, follow the instructions with the pip documentation. If you’re using the Blender python installation you will have to run the ensurepip script.

  • zstandard python package — pip install zstandard

WHERE TO PUT THE SCRIPT


Sigh, so, I guess I need to go over this. Any program you want to be able to use at the command / terminal prompt must be in a directory indexed via the PATH environment variable. I would like to assume you know what all this is…
But in all fairness I can’t do that.

There is no One True Way™ to do this. What follows is how * I * do it.

Windows

If you wish to make the script available to all users, create a folder named:
C:\Users\Public\bin

If you only want to use it yourself:
C:\Users\USERNAME\bin
⟶ Replace “USERNAME” with your own username, of course.

Now we need to add that directory to the PATH environment variable.

Unfortunately, there is no one obvious way to get to the system environment variables in Windows. For me, clicking Start and typing “env” is sufficient to get a suggestion to “Edit the system environment variables”.

If that doesn’t work you’ll need to open the Control Panel and click at things named “System” until you find the System Properties dialog, which has a button at the bottom labeled Environment Variables…

Once you get the Environment Variables dialog up you will see it has two parts: the top is for your user-specific environment variables and the bottom is for the system environment variables for all users.

In the correct list there needs to be a variable named “Path” (capitalization doesn’t matter). Add it if missing.

The PATH is a list of absolute directory names separated by semicolons ;. Add the path of the directory you created to the FRONT of the list. The value will now look something like:
C:\Users\Public\bin;C:\Windows\system32;C:\Windows;
or
C:\Users\Michael\bin

Click OK and restart your PC.

Linux & OS X

If you wish to make the script available to all users, put it in:
/usr/local/bin

If you only want to use it yourself, stick it in:
~/.local/bin

Your system should already have those in your PATH. If not, edit ~/.bashrc (or ~/.profile) and add the directory to the FRONT of the list (separated by colons :) so that it reads:
PATH="$HOME/.local/bin:$PATH"

Exit the terminal emulator and start a new instance.


THE SCRIPT HEADER


We are almost there. Honest.

To make the script work at the command / terminal prompt, there needs to be a little magic at the very beginning of the file to make it execute properly.

Windows

In the bin directory you created, create a text file named “bver.bat” and paste the following text into it:

@echo off
rem = """
  python -x "%~f0" %*
  exit /b %ERRORLEVEL%
"""

Linux & OS X

In the bin directory you identified, create a text file named “bver” and paste the following text into it:

#! /usr/bin/env python

Set its permissions to ‘executable’. You can do this from the prompt with:
chmod +x ~/.local/bin/bver or
chmod +x /usr/local/bin/bver, whichever is appropriate.


THE SCRIPT BODY


Paste the following text into the remainder of the file and save:

#! /usr/bin/env python3

# v1.0 original version
# v1.1 updated to handle Blender 5.0's new .blend file header
# v1.2 heh, not all .blend files start with a REND block. Fixed.

"""
usage:
  bver FILENAME.blend ...

States the version of Blender used to create each FILENAME and
whether or not it is compressed.

".blend" is auto-appended to FILENAME if not specified.

Compression methods:
  Zstandard • created by Blender versions 3.x onward
  Gzip      • created by Blender version prior to 3.x

Terminates with exit code 0 if all files successfully versioned, else 1.
"""

import sys, re, pathlib, zstandard, gzip

files_not_found, files_not_authorized, files_invalid = [], [], []
decompressor = None


# Display help?

if ((len(sys.argv) < 2) or ("--help" in sys.argv) or ("/?" in sys.argv)):
	print(
		re.sub(r'\bbver\b',
		pathlib.Path(sys.argv[0]).stem,
		__doc__.strip()))
	exit(0)


# Pretty-print the version and compression status...
# Blender version strings have one of TWO formats:
#
#   BLENDER-v305REND
#   BLENDER17-01v0500REND
#
# The new file format is incompatible with the older one, hence the change.
#
# I am unsure exactly when this occurs. B4.5 can evidently -read- the newer
# file format, but it naturally produces the older. B5 only writes the newer.
#
# I also cannot (quickly) find any documentation about the meaning of the
# numbers "17" and "01" and whether they change.

version_re = re.compile(r'^BLENDER(?:\d\d)?[_-](?:\d\d)?[vV](\d\d?)(\d{2})(.)')

def id_blend_file(bytestring, filename):
	s = bytestring.decode(encoding='utf-8', errors='ignore')
	R = version_re.match(s)
	if R:
		rc = R.group(3)
		return (
			int(R.group(1)), # major
			int(R.group(2)), # minor
			rc if rc.islower() else '' # revision patch code ('a', 'b', ...)
		)
	else:
		raise Exception()

def print_version(bytestring, compression_status, filename):
	major, minor, rc = id_blend_file(bytestring, filename)
	ms = f"{minor}{rc}"
	print(f"  {major}.{ms:<3} {compression_status} {filename}")


#--------------
# Main program
#--------------

# for each file named on command line...
for filename in [pathlib.Path(arg) for arg in sys.argv[1:]]:

	# Skip directories
	if filename.is_dir():
		continue

	# Auto-append ".blend"
	# (File name MUST end in .blend, .blend1, etc)
	if not re.fullmatch(r'\.blend\d*', filename.suffix):
		filename = filename.with_suffix(filename.suffix + '.blend')

	try:
		with open(filename, mode='rb') as f:

			try:
				# ID an uncompressed Blender file
				print_version(f.read(16+5), '-', filename)

			except:
				# Initialize the Zstandard decompressor & file reader
				f.seek(0)
				if not decompressor:
					decompressor = zstandard.ZstdDecompressor()
				reader = decompressor.stream_reader(f)

				# ID a Zstandard compressed Blender file
				try:
					print_version(reader.read(16+5), 'z', filename)

				except:
					# ID a Gzip compressed Blender file
					f.seek(0)
					with gzip.GzipFile(mode='rb', fileobj=f) as g:
						print_version(g.read(16), 'z', filename)

	except FileNotFoundError: files_not_found     .append(filename)
	except PermissionError:   files_not_authorized.append(filename)
	except:                   files_invalid       .append(filename)

# Print file names not found / not authorized / invalid ?

# Exit code == any errors?

exit(
	(len(files_not_found) != 0) or
	(len(files_not_authorized) != 0) or
	(len(files_invalid) != 0))

That’s it!


USAGE


Using the script is as easy as using any other command-line utility. From the command / terminal prompt change to the directory containing the .blend file(s) you wish to version, and type:

bver myfile.blend

or

bver *.blend

etc. You will see a pretty list of version numbers and filenames, like:

  3.5   - myfile.blend
  2.75a - another.blend
  4.5   z awesome.blend

The z indicates that the file is compressed.



Hope this is helpful to y’all.

4 Likes

Update for 5.0+

Shortly after I released this script I fired up my brand-new copy of the just-released Blender 5.0 and… discovered it didn’t work (the script, that is).

That’s because Blender 5.0 is sufficiently advanced that the devs have updated Blender’s file format in a way incompatible with previous versions! To emphasize the point the .blend file header has changed.

Never fear, though, I haxxed it again.

I am currently working on a little GUI app that’ll be a whole lot more convenient than this thing — presenting an actual dialog to start the desired Blender version when you click on a .blend. It’ll have options and be pretty.

I’ll post here with a link to the topic where you can download the script when it’s available.

Minor bug fix. Turns out that not all .blend files start with a “REND” block, heh.

(I discovered the error when I tried to ID a userpref.blend.)