2

I am using "Window Command shell" and "Bash shell" in windows 10 OS. What is really annoying me on this, I have to define alias for each shell (doskey & alias), even I am using same command & folder alias. For example, alias np='D:_MyProgram_IDEditor\Notepad++\notepad++.exe' doskey np=D:_MyProgram_IDEditor\Notepad++\notepad++.exe $*

Therefore, I'd like to know how to define common definitions which will work in both shell. Currently my idea is this...

  1. define common definition text file
  2. make a script that read "common definition file" and define "doskey in Commands", and "alias in Bash shell".

Is there good way to do it or any related work about this?

Example)

1. define common definition text file (common-define.txt)

"SET"   "DIR_PROGRAM"   "D:\OneDrive\_MyProgram"
"CD" "dev"              "D:\OneDrive\_MyProgram"
"ALIAS" "tt"            "tt.bat"


2. read it and define "doskey in commands", and "alias in shell" (autoload-batch.bat)

FOR /f "tokens=1,2,3* delims= " %%A IN ( common.define.txt
) DO (
if "%%~A"=="###"    echo %%A, %%B, %%C
if "%%~A"=="SET"    set %%~B=%%~C
if "%%~A"=="SETX"   setx %%~B %%~C
if "%%~A"=="CD"     echo @doskey %%~B=cd /d %%~C>>gen_doskey.cmd
if "%%~A"=="ALIAS"  echo @doskey %%~B=%%~C $*>>gen_doskey.cmd
)

reg add "hkcu\software\microsoft\command processor" /v Autorun /t reg_sz /d %~dp0\gen_doskey.cmd /f

this is windows version. and I am working on linux version. What I am think of doing this, Is there any good way to do it?

OfusJK
  • 676
  • 1
  • 5
  • 13
  • Not sure if the Linux tag would help you more here. – John Kens Dec 13 '18 at 03:17
  • 1
    If Cygwin bash uses the Windows console (i.e. conhost.exe) and reads via high-level `ReadConsole`, then you can use doskey.exe for it as well (or, equivalently, `AddConsoleAlias` in C). That said, it's just a console input alias that matches at the beginning of the line for simple commands. It can't be used anywhere else on a line (e.g. in a pipe), and it can't be used in a script. – Eryk Sun Dec 13 '18 at 14:29

1 Answers1

3

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.

rojo
  • 24,000
  • 5
  • 55
  • 101
  • Thanks. it would help me. I will do a study of this and leave feedback here. – OfusJK Dec 17 '18 at 07:34
  • I had tried first approach and I got the path problem, as you already expected. and change the path in batchfile like this `: <<'BATCH_PORTION' :batch @echo off for /f "tokens=2* delims== " %%I in ( 'findstr "^alias" "%~f0"' ) do ( set "T=%%J" call set "T=%%T:/=\%%" doskey %%~I=%%T%% )` and found another issue like changing drive with 'cd' command in windows system "cd /d path". Now, I think it takes more time to get a solution proper to me than I expected. also I found another way to do it. [link](https://github.com/gajop/batch2bash/tree/stable/examples). – OfusJK Dec 18 '18 at 08:49