PyBat: A Python Module to Replace Batch File Usage (or, “Batch Files Aren’t Dead, They Just Deserve to Die”)

Download PyBat 0.1.2

More details at: http://pybat.sourceforge.net

UPDATE: Hi, Reddit! I just wanted to clear this up: PyBat is about solving a somewhat specific problem: allowing Python scripts and batch files to communicate environment variable changes. This is in the case where you have a ton of legacy batch files that you cannot (for whatever reason) outright replace (which may or may not be the best thing to do in your case). I understand that everyone has their favorite scripting language (and for nontrivial tasks, they all sound better than batch files), but this is for situations where you want new Python scripts to work with the old stuff. The rest of the article is simply my rant that using batch files for nontrivial tasks is like working on a fusion reactor with a stone ax. Thanks for reading! Check out the rest of the site!

I’ve seen build systems that have accumulated the cruft of undocumented, half-forgotten, it-was-written-by-a-guy-who-no-longer-works-here software that was added piecemeal over time. It wasn’t until I had to do maintenance work on it that I discovered the horrifying truth of batch files.

Batch files are great for small tasks. But no bit of software remains small for very long, and after a while it balloons into an almost (but not quite) unbearable size. Most batch files rely on goto logic. Batch files use environment variables (which is essentially a single namespace of global variables). Batch files have no consistent syntax. Batch files require several twists and hoops for basic programming tasks, if they can do them at all. Batch files rely on obscure command line switches (I can never remember if copy’s /Y suppresses the confirmation prompt and /-Y enforces the confirmation prompt, or the other way around.)

(UPDATE: I know you can use “call” to jump to a label in the batch file, though I don’t see it in use often. The parameters use the %1 %2 etc. convention, instead of more descriptive labels. And although you can always /? a command, most command line switches are also not very descriptive.)

I tried googling for a batch file debugger, but only found pages that give handy “techniques” like printf-style debugging. Yech.

Of course I then realized the reason no one would make such a useless, baroque thing is because we already have real scripting languages with real debuggers. Although throwing out a large system of batch files and rewriting them in Python or Perl is tempting, it’s something you should almost never do unless you have a week or two to kill.

No problem. Let’s just make all our new code or do piecemeal replacements in Python, and leave the old but still usable batch files around.

But you can’t. Because of environment variables. Many of the batch files I’ve seen do nothing but configure settings by changing environment variables (Visual Studio’s vcvars32.bat is an example of this). But any changes made to a child process’s environment will not be seen by the parent process. Batch files calling other batch files get around this with the “call” command. But batch files calling Python scripts (or vice versa) will not be able to see any changes made by the called file.

It’s not a flaw at all, it’s a basic, well-designed operating system principle. But it does make adding Python scripts that are backwards compatible with a batch file system impossible.

So I wrote a script to get around this impossibility. PyBat is a Python module that lets a Python script call a batch file (or vice versa), and will see the changes made by the called file. Because batch files can share environment variables with each other (with the call command), and Python scripts can share environment variables with each other (with the execfile() function), I’ve written pybat.py and pybat.bat that can bridge the two worlds by writing to a commonly known file. (A hack approach, but also the simplest that works.)

PyBat is almost completely concurrency-safe (there is an astronomically small chance that running multiple instances will conflict, even for high loads), but quite usable as duct tape between Python and batch files.

Feel free to download the documentation and code and give it a try yourself, and please send any feedback you have. PyBat is open source, and released under a BSD-style license.

Download PyBat 0.1.1

Also, as further incentive to move on to Python, I also have prepared a small Python cheatsheet for programmers and a Python FAQ for batch filer writers to help developers get up to speed with Python.

8 Responses to “PyBat: A Python Module to Replace Batch File Usage (or, “Batch Files Aren’t Dead, They Just Deserve to Die”)”

  1. mschaef Says:

    “Batch files rely on goto logic.”

    Not entirely true. Windows NT style batch files offer subroutine calls with local variable scopes too. They also offer multi-line if statements, etc.

    “Batch files use environment variables (which is essentially a single namespace of global variables).”

    setlocal lets you isolate environment variable changes to the currently running batch file.

    “Batch files rely on obscure command line switches (I can never remember if copy’s /Y suppresses the confirmation prompt and /-Y enforces the confirmation prompt, or the other way around.)”

    Type ‘help copy’ at the command prompt to get a detailed description. Type ‘help’ to get a list of help topics.

    Batch files still suck, but they suck a lot less than they used too.

  2. al Says:

    Response to mschaef:

    Yes, but at the same time the batch file call statement doesn’t support parameters. You could use an env. var. like %param1% and %param2% and so on. This isn’t a good standard though.
    The setlocal and endlocal commands gives you a new namespace for variables, but I find them as tedious and typo-prone as curly braces for block delimiters.

    And command line switches are documented, but you have to look up the documentation. It is not apparent what behavior they dictate just from looking at it. You can use the long form, but then you have a single line of long, unreadable text for one command.

    And batch files still only place nice with other batch files with regards to environment variables (hence PyBat).

    Many times it just feels like writing batch files is as tedious as coding in assembly.

    We agree on the basic point, but I still lean much towards the “sucks” side. :)

  3. josh Says:

    “the batch file call statement doesn’t support parameters.”

    Yes it does.

  4. al Says:

    “the batch file call statement doesn’t support parameters.”

    “Yes it does.”

    My goof, the call statement does support parameters with %1, %2, etc. My beef is however that these names are not descriptive as to what the subroutine uses them for. (Again, batch file writing takes on that assembly programming feeling.)

    Which in the case of simple, small batch files is fine. However, it approaches a point where batch files demanding this level of complexity begin to become too unwieldy.

  5. Craig Says:

    Since you’re working on a build system, why reinvent the wheel? Just use: ant, nant, pyant, rake or one of the other build tools out there. Now you’re just adding a task to set environment variables (which some of them already sport) rather writing the entire script yourself.

    And yes, batch files should die ;)

  6. al Says:

    craig: I thought other people might have this same problem, so I worked on this project on my own, making it general enough for public use but also usable for stuff at work.

    I did this precisely to not reinvent the wheel. I don’t want to rewrite batch files as Python scripts, but this environment variable problem always stood in the way of moving the team at work to Python and away from even more batch files. PyBat is a lot of work up front, but it could also be helpful to anyone on the web with a similar problem.

  7. Todd Says:

    I’m seeing that os.system() doesn’t work after a call to pybat.callbat()

    example
    ——-
    print os.system(“echo blah”)
    print pybat.callbat(“some.bat”)
    print os.system(“echo blah”)

    output
    ——
    blah
    0
    -1

  8. Sarfraz Says:

    HI
    When i tried to use Pybat and follow your instructions for bat to python.
    it is not working and not setting Environment variable. i also send you email.
    Can you please respond. Thanks
    Best Regards
    Sarfraz

Leave a Reply

free blog themes