Skip to content

Instantly share code, notes, and snippets.

@andyneff
Last active May 16, 2024 15:45
Show Gist options
  • Save andyneff/fafba17b748bba6d7cd5 to your computer and use it in GitHub Desktop.
Save andyneff/fafba17b748bba6d7cd5 to your computer and use it in GitHub Desktop.
Hybrid script files

Hybrid files

  • A short compilations of hybrid files I've used/modifies whats out there for my use.

What do I mean by hybrid files? A file that can be parsed by two or more scripting lanauges and run (without or minimal error messages)

Batch and python

I copied this from here There are many different versions out there, of varying degrees of difficulty out there. However this is my favorite

1>2# : ^
'''
@echo off
setlocal
REM batch code goes here
python "%~dpf0" %*
echo %cmdcmdline% | find /i "%~0" >nul
if not errorlevel 1 pause
exit /b
'''

#Python code goes here

The first line it the magic. Based on the comments, it works as follows. In python, 1>2 is evaulated, then a comment (#). Straight forward. Now in batch, it's a a little tricker.

  1. Apparently you can redirect at the begining of a command in batch AND bash? link

  2. 1>2# Redirects stdout to the file names 2#

  3. : is a label in batch, and according to jeb

    for batch the colon is the null op, this removes also the 1>2# so that the batch doesn't try to open the file 2#

  4. The ^ is escape character in batch, so it is escaping the newline (the ^ must come last, of course, no spaces). And so it's more like reading 1>2# : \n '''

The two lines for %cmdcmdline% pause are optional. In my case, I sometimes run the script in a command line prompt, and I also sometimes double click an icon. When you double click, the window pops up and disappears so fast, you don't get to read the output. This will pause only when you double click the icon. I think they are some loopholes to where it still misses it, but it works for me.

The %* is limited to what batch can handle. No silver "${@}" bullet here :( Again, the python line is only if you always want to execute this script in python when you call it in batch. Your use case may be different

Bash and python

#!/usr/bin/env bash

_=''''

# Bash code here

exec /usr/bin/env python $0 "${@}"
'''
#!/usr/bin/env python

# Python code here

Visual Basic (cscript) and batch

Based on this

:' Put comments here
:'^
a=1_
<1' echo off <nul 

set n=nothing' & goto :'batchCode & REM Jump to the batch code

Set UAC = CreateObject("Shell.Application")
UAC.ShellExecute WScript.ScriptFullName, Wscript.Arguments.item(0), "", "runas", 1
WScript.quit(0)

:'batchCode
set n=n'& setlocal
set n=n'& net session >nul 2>&1
set n=n'& set PATTERN=VirtualBox
set n=n'& if not %errorlevel% == 0 (
  set n=n'& if "%attemptedElevate%" NEQ "1" (
    set n=n'& set attemptedElevate=1
    set n=n'& echo "Elevating permissions..." 
    set n=n'& cscript /e:vbs %~f0 %PATTERN%
  set n=n'& exit /b 1 ) else (
    set n=n'& echo "Elevating permissions failed" ) )

set n=n'& echo "Batch code here!"

Don't know much about VB, but ' is the single line comment character, and : must be nothing-ish. So the :' lines are ignored by batch for the : and VB for '. a=_ is ignore in batch because the previous line ended with ^ and in VB, _ is the "continued on the next line" character, so it really become a=1<1 and the rest ignored by comment. In batch, <1' is a redirect, echo off disables echoing and <nul replaces the bogus 1' from failing to be read from? set n=nothing' is both a valid set object command in VB, and set variable in batch. the goto is already commented out in VB but executed in batch. From there on, only VB is executed. The VM section should end in a quit, and then the reset is batch. Every line must be VB compiled, so all batch lines must start with set n=n'& to essentially do nothing, but escape out the real batch commands. This script is the typical reason WHY you'd want to do this, to elevate permissions. Otherwise, you'd have to write a temp file. This version does not currently handle multiple arguments, but you could write a VB loop to do that, but VB butchers a lot of the arguments, including removing quotes.

Bash batch hybrid

Improvement made upon this

: '"
@echo off
goto Batch
"'

echo Bash Code
exit 0

:Batch
echo Batch Code
exit /b 0

Now, I usually have my code checked out in git, and use the newline magic of modern git to handle this. If that doesn't work for you, you will need at at least add

: '"
@echo off
goto Batch
"' #
#
echo Bash Code #
exit 0 #

:Batch
echo Batch Code
exit /b 0

Bash, batch and python

This one I came up with combining a few different tricks. I think I combined previous tricks with this to get one file to run them all

:; /usr/bin/env python -x $0 "${@}"; exit $? ;  ^
'''
@echo off
python -x %~dp0%~nx0 %*

echo %cmdcmdline% | find /i "%~0" >nul
if not errorlevel 1 pause
goto :eof
'''

#Python part

How it works, language by language by language

Batch

The first line is a label, and is continued to the second line. So they are both ignored, silently. The next lines are all normal batch commands like in the previous. In this case, I always wanted to call python on the same script, although your use case may be different. Python is called with -x, else python will error out on compiling the first line. %~dp0%~nx0 expands out to be the full path. I feel like there is a reason I did not use %~f0, I want to say it has something to do with relative paths, but I don't remember. Same %cmdcmdline% trick. The end says goto :eof, which ends the script. This could also be exit /b 0 too, just another option.

Bash

: evaluates as true, The semicolon begins the next command to be executed./usr/bin/env python takes the place of what a normal #! would do. Of course if you didn't want to run the script in python when called from bash, you can put anything else you wanted in here instead. But in this case, it runs it in python. To do this, we have to add -x so that python will ignore the first line. Without the -x, python will error out when compiling. We add $0 so the same files is used to execute in python, then the bash silver bullet, "${@}" to pass all the arguments alone, how they were passed in. Exist with $? to pass the exit code, and the rest, ^, never gets executed.

Python

Python has to be called with -x to skip the first line, then the batch lines are interpreted as a multi-line string, and ignored, and the rest if python!

CONS:

  • Now, this works as long as you don't need to import the file.
  • If you call python manually and want to run this, you have to remember to add -x
  • Just like you can't import it, you can't used -m because it most likely won't have a .py extension, plus you can't combine -m and -x to get a desired effect, at least in python 2.7 -You only get ONE line for Bash

Other than that, it works GREAT!

Why???

Is why not a valid answer? No, I suppose not... The answer is tab completion. I worked on projects both windows and linux, and they needed to have the same functionality. So it makes sense the commands would be the same too... And the scripts would get execute permissions (not that that mattered in windows), and depending how you did it, either tab complete wouldn't pick the right extension for you or it just wouldn't in windows, and you had to type it in differently every time you switched back and forth, and in the end I put everything in python, and while linux has shebang, windows doesn't. And while windows has the ability to associate and make another extension executable, that's MODIFYING the OS, and I like to stick with "You shouldn't required your used to modify the OS just to run your software" as much as possible, and these hybrid files allowed be to have the best of three worlds :) So that's WHY.

I wanted to be able to type ru<tab> to tab complete run_server.bat and do the EXACT same on both OSes, and really run my python code underneath.

Actually, I'm not really a horse. I'm a broom