George V. Reilly

Python Batchfile Wrapper

Python Batchfile Wrapper

I’ve been getting into Python lately. One problem that I’ve en­coun­tered under Windows, is that input redi­rec­tion doesn’t work if you use the .py file as­so­ci­a­tion to run the script; e.g.:

C:\> < input.txt

There’s a well-known input redi­rec­tion bug. The fix is to explicitly use python.exe to run the script.

A related problem for me was that stdin was opened as a text file, not a binary file, so \r bytes were being discarded from binary input files. The fix is to run python.exe -u (unbuffered binary input and output).

I didn’t want to hardcode the path to python.exe in a batch file, so I came up with the following wrapper, which parses the output from assoc .py and ftype Python.File.

Just place this batch file in the same directory as and call it foo.bat.

@if (%_echo%)==()  set _echo=off
@echo %_echo%

:: You must explicitly invoke python.exe, rather than rely on the
:: file association for .py, if you want stdin redirection to work.
:: See
:: The -u flag to python.exe specifies unbuffered, binary stdin,
:: so '\r\n' is not remapped to '\n'.
call :FindPythonExe

if "%PythonExe%"=="" (
    echo Can't find python.exe
    exit /B 1

:: Replace the extension of this batch file with .py: s/.bat$/.py/

"%PythonExe%" -u %PythonFile% %*
goto :EOF

:: Find python.exe in the path or via the .py association
set PythonExe=
:: Search for python.{cmd,bat,exe} in %PATH%
for %%i in (cmd bat exe) do (
    if "%PythonExe%"=="" (
        for %%j in (python.%%i) do set PythonExe=%%~$PATH:j
:: Extract path to python.exe from .py association
if "%PythonExe%"=="" call :AssocPy2Exe
goto :EOF

:: Return the executable associated with .py in %PythonExe%
call :AssocExtn2Exe .py
set PythonExe=%_exe%
goto :EOF

:: Return the executable associated with file extension %1 in %_exe%
:: assoc .py -> .py=Python.File
for /f "usebackq tokens=2 delims==" %%i in (`assoc %1`) do set _ftype=%%i

:: ftype Python.File -> Python.File="C:\Python24\python.exe" "%1" %*
:: Grab everything after the '='
for /f "usebackq tokens=2 delims==" %%i in (`ftype %_ftype%`) do set _rhs=%%i

:: Get the first token of the space-separated list
for /f "tokens=1" %%i in ("%_rhs%") do set _exe=%%i
goto :EOF

Now you can run foo.bat < bar.jpg with the expected results.


Update 2006/01/03: The batchfile now searches %PATH% before looking up the .py as­so­ci­a­tion.

Update 2007/01/12: See here for a sig­nif­i­cant­ly improved batchfile and for py2cmd.

blog comments powered by Disqus
Installing Rainlendar on Kubuntu » « Rating with Stars