70
$\begingroup$

Let's say we have a Python script my_script.py that does some data processing with Blender. This script accepts some arguments (e.g. arg1 arg2). How can I forward arguments to the script when using it with the CLI?

blender --background --python my_script.py

The only trick I could find was this one:

echo "arg1 arg2" > my_input.txt && blender --background --python my_script.py

Is this really the only way to do that?

Update

I found a similar question on Stack Overflow.

$\endgroup$

5 Answers 5

86
$\begingroup$

Yes, Blender's Python can read command line arguments.

In summary:

  • Python can read all arguments passed to Blender via sys.argv
    (just the same as you would in Python)
  • Blender will ignore all arguments after: --
    (double dash with no arguments, as documented in the --help message)
  • Scripts can check for -- in sys.argv and ignore all arguments beforehand.

So Python and Blender always see the same arguments, Blender knows not to interpret arguments after -- and as the script author it's up to you not to interpret Blender's arguments before --.

This is done so other regular Blender arguments can be passed after --python, so you could for example pass:

blender --python script.py --render-frame 2..10

... to run a script then render frames 2 to 10.


Example:

Script: mytest.py

import sys
argv = sys.argv
argv = argv[argv.index("--") + 1:]  # get all args after "--"

print(argv)  # --> ['example', 'args', '123']

Execute like this:

blender --background test.blend --python mytest.py -- example args 123

Having spaces around -- is important, this is a signal that Blender should stop parsing the arguments and allows you to pass your own arguments to Python.


Further information:

For a more comprehensive script example, background_job.py is a Python template which comes with Blender, this uses Python's argparse module, for more flexible handling of arguments.

If you want to have comprehensive arguments for your script with a --help message, Look into argparse, general Python docs on the module can be used.


Note: if -- is not always needed, you can check for it like this.

import sys
argv = sys.argv
try:
    index = argv.index("--") + 1
except ValueError:
    index = len(argv)

argv = argv[index:]

Note that using argparse is optional, you can simply do checks such as:

if "--myarg" in argv:
    do_stuff()

... but I've found as soon as you want to pass values to arguments, this becomes a hassle and its generally better of to use argparse to begin with, unless...

  • You're making a quick test and only need primitive argument handling.
  • You have a good reason to spend time doing your own argument parsing, and argparse can't handle your use-case (although this is rare in my experirnce, as argparse is flexible enough for most use-cases).
$\endgroup$
2
  • 1
    $\begingroup$ +1 for the solution. So you mean that if you don't add the option "--", both Python and Blender will interpret the options following after "--python"? What's the point of that? That makes it feel like this feature is either not really thought through or not an intended feature. Is it documented somewhere? $\endgroup$ Jun 27, 2018 at 11:07
  • $\begingroup$ Updated answer to explain why this is done. $\endgroup$
    – ideasman42
    Nov 10, 2021 at 23:44
12
$\begingroup$

thanks for the suggestions. I just managed to get a working example following the double-dash idea that retains all of argparse's functionality with minimal intrusion. Works for me, let me know if you see any issues!

my_script.py

import argparse
import sys

class ArgumentParserForBlender(argparse.ArgumentParser):
    """
    This class is identical to its superclass, except for the parse_args
    method (see docstring). It resolves the ambiguity generated when calling
    Blender from the CLI with a python script, and both Blender and the script
    have arguments. E.g., the following call will make Blender crash because
    it will try to process the script's -a and -b flags:
    >>> blender --python my_script.py -a 1 -b 2

    To bypass this issue this class uses the fact that Blender will ignore all
    arguments given after a double-dash ('--'). The approach is that all
    arguments before '--' go to Blender, arguments after go to the script.
    The following calls work fine:
    >>> blender --python my_script.py -- -a 1 -b 2
    >>> blender --python my_script.py --
    """

    def _get_argv_after_doubledash(self):
        """
        Given the sys.argv as a list of strings, this method returns the
        sublist right after the '--' element (if present, otherwise returns
        an empty list).
        """
        try:
            idx = sys.argv.index("--")
            return sys.argv[idx+1:] # the list after '--'
        except ValueError as e: # '--' not in the list:
            return []

    # overrides superclass
    def parse_args(self):
        """
        This method is expected to behave identically as in the superclass,
        except that the sys.argv list will be pre-processed using
        _get_argv_after_doubledash before. See the docstring of the class for
        usage examples and details.
        """
        return super().parse_args(args=self._get_argv_after_doubledash())

parser = ArgumentParserForBlender()


parser.add_argument("-q", "--quack",
                    action="store_true",
                    help="Quacks bar times if activated.")
parser.add_argument("-b", "--bar", type=int, default=10,
                    help="Number of desired quacks")
args = parser.parse_args()
QUACK = args.quack
BAR = args.bar

if QUACK:
    print("QUACK " * BAR)

The following calls will work as expected (tested on Ubuntu 17):

blender --python 'my_script.py'
blender --python 'my_script.py' --
blender --python 'my_script.py' -- -q
blender --python 'my_script.py' -- -b 100
blender --python 'my_script.py' -- -q -b 100
blender -b --python 'my_script.py' -- -q -b 100 # note that -b is unambiguous

Cheers!
Andres

$\endgroup$
4
  • $\begingroup$ Cool. If installed as addon (or via another method to place file in blenders python path) can from my_script import args in any script to get the parsed script args. $\endgroup$
    – batFINGER
    Mar 18, 2019 at 14:39
  • $\begingroup$ How would then look the CLI call? can an addon be called with args upon blender start? $\endgroup$
    – fr_andres
    Mar 18, 2019 at 17:15
  • $\begingroup$ As for CLI blender --python somescript.py -- -q -d 100 in "somescript.py" from my_script import args I imagine that in any addon could do same, just like importing from any other module. $\endgroup$
    – batFINGER
    Mar 18, 2019 at 17:28
  • 1
    $\begingroup$ I understand, thanks! so this way I can pack my personal utilities as module into the blender path to avoid defining them in every script. Nifty $\endgroup$
    – fr_andres
    Mar 18, 2019 at 17:30
4
$\begingroup$

Another way would be to set environment variables for the arguments

SET arg1=val1
blender -P script.py

Within the script the environment can be queried using os.getenv()

import os
val = os.getenv('arg1')
print(val)
$\endgroup$
1
  • 1
    $\begingroup$ While this works, there are a few minor downsides, 1) typos in env vars easily go unnoticed. 2) you have to do your own type checking on the contents of values (if some are numbers for eg). 3) they don't get added to a --help message automatically (as with argparse). 4) Setting them isn't the same across all platforms, so your documentation needs to account for that. $\endgroup$
    – ideasman42
    Jan 31, 2016 at 1:16
2
$\begingroup$

I wrote something for blend_stats, which you could use:

import sys

# Get script parameters:
# all list items after the last occurence of "--"
print()
print(sys.argv)
print()

try:
    args = list(reversed(sys.argv))
    idx = args.index("--")

except ValueError:
    params = []

else:
    params = args[:idx][::-1]

print("Script params:", params)

Example call:

blender.exe --background test.blend -P ..\blend_stats.py -- foobar script_param_2 1337

Note the space between -- and foobar!

$\endgroup$
9
  • 1
    $\begingroup$ use of reversed and exception handling here seems unnecessary. $\endgroup$
    – ideasman42
    Apr 8, 2014 at 20:45
  • $\begingroup$ So what is your recommended solution? There is no rindex() or rfind() for lists. I could join the list args = " ".join(sys.argv) and do args[args.rfind(" -- ")+1:].split(" "), but it's not very readable and it incorrectly returns something if the separator isn't present. And it messes up if the separator and spaces occur in the path to blender binary (sys.argv[0]). $\endgroup$
    – CodeManX
    Apr 8, 2014 at 21:29
  • $\begingroup$ Simply use argv.index("--"), see my answer below. (Blender stops parsing after the first --, so this is correct). $\endgroup$
    – ideasman42
    Apr 8, 2014 at 21:50
  • $\begingroup$ But it fails if there's no "--". For blend_stats, it was my intention to use the very last occurrence of "--", not the first, so I had to come up with above code, as there's no rindex(). $\endgroup$
    – CodeManX
    Apr 9, 2014 at 2:10
  • 1
    $\begingroup$ Ah, my bad :( - but even then - using reversed like this is incorrect. Even if in practice its mostly fine, its misleading WRT blender's own logic $\endgroup$
    – ideasman42
    Jul 22, 2015 at 20:32
0
$\begingroup$

I create a video taking inspiration from the comments here nothing really worked exactly for me but i found a way it did work for me and I wanted to share it. https://www.youtube.com/watch?v=PnUQUNtQ38E

I used a bash script to call a python script to start blender file with an auto-run python script.passing the arguments through each.

Bash Script:-------------------------------------------------------------------------------

    ~$ ./P_to_B.py --filenameArg

Python Script:-------------------------------------------------------------------------

    #! /usr/bin/python3
    import sys
    import subprocess

    print(sys.argv)

    subprocess.run(["blender", "MyBlendfile.blend", '--',] + sys.argv)

Blender File:------------------------------------------------------------------------------- import sys

     print("This is our argument list: ", sys.argv)
     print("This is from the blender file!!!!!!!!!!!!!!")
$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .