Native Linux, translated for doskey
I believe I've successfully created a batch + bash polyglot script. Save this using a bash shell so that lines are terminated with LF only, not the Windows-style CRLF. It seems cmd is more accepting of Unix-style line terminations than bash is of Windows.
: <<'BASH_PORTION'
@goto batch
BASH_PORTION
# aliases
alias hello="echo Hello world!"
alias ls="dir"
: <<'BATCH_PORTION'
:batch
@echo off & setlocal
for /f "tokens=2* delims== " %%I in (
'findstr "^alias" "%~f0"'
) do doskey %%~I=%%~J
exit /b
BATCH_PORTION
Then to source the script, in a bash script do source scriptfile.bat
. Indeed, that's how ~/.bashrc and ~/.profile get sourced when you launch a new bash session. In a cmd shell you can just execute scriptfile.bat
.
The magic happens with the careful placement of heredoc multi-line comments. The bash interpreter sees : <<'BASH_PORTION'
as the beginning of a multiline comment and BASH_PORTION
as its end. I got the idea from this answer. Using those heredoc comments, bash can be coerced into ignoring the cmd batch-only lines. And batch treats any line beginning with a :
as a label, so the first line has no effect.
After sorting out tricking each interpreter into ignoring the other's commands, all that's left is to define the aliases. Aliases must be defined using double quotes on the bash side, then the quotes can be stripped using a for /f
loop on the batch side with alias
being replaced by doskey
.
Native doskey, translated for Linux
Alternatively, you could just create a macro file in doskey format as normal, something like:
hi=echo Hello $*
bye=echo Goodbye $*
To load the macros to the cmd console, you'd just
doskey /macrofile="path\to\macrofile"
... which you could put into "HKLM\SOFTWARE\Microsoft\Command Processor" in the Autorun value if you wish.
To load the macros in your Linux subsystem takes a little massaging to translate and source. Something like this would go into your ~/.bashrc:
t=$(mktemp -p /dev/shm)
awk -v RS='\r\n' -F\= '{print $1"(){ "$2"; }"}'</mnt/c/Users/path/to/macros.doskey >$t
source $t
rm -f $t
unset t
Unfortunately, I haven't been able to find a solution using eval
to define functions or aliases -- I'm guessing because eval
spawns a new shell that doesn't pass environment changes to the calling scope. So the temp file seems to be required.
An example adding file path translation
With the second method, if you want to translate Windows filesystem paths into Linux notation, you can do so by adding a couple of gsub
commands. Take the following macros.doskey example:
hi=echo Hello $*
bye=echo Goodbye $*
desktop=pushd "c:\Users\username\desktop" && dir && popd
In the Windows side, you'd still source it the same.
doskey /macrofile="path\to\macros.doskey"
In the Linux side, you'd put the following in your .bashrc or similar where it will be evaluated using source filename
:
alias dir='ls --color=auto'
t=$(mktemp -p /dev/shm)
awk -v RS='\r\n' -F\= '
{
gsub(/[A-Za-z]:\\/, "/mnt/&")
gsub(/\\|:/,"/")
print $1"(){ "$2"; }"
}
'</mnt/c/path/to/macros.doskey >$t
source $t
rm -f $t
unset t
And that will replace all backslashes with forward, replace c:\ with /mnt/c/ and similar, and create functions using the massaged data. A big thing to watch out for, the drive letters in your macros file need to be in the same case as they appear in /mnt. Also, I tested these scripts using gawk
. By default, many Linux distributions use mawk
as the included awk intepreter. I tried to make the scripts awk-agnostic, but I'm not sure whether I succeeded or not. In any case, it might be a good idea to install gawk from your chosen distro's repositories and remove mawk. mawk is a minimalist implementation of awk, missing a lot of functions.