How to run windows executables from terminal without the explicitly specifying the .exe extension?

You have three options.

  1. Always type the .exe. Command completion might help, assuming your shell has it. But this is what you are trying to avoid.

  2. The alias solution already pointed out by others.

  3. Make symbolic links without the .exe to the Windows executables. These should be placed in a directory (on a Unix/Linux filesystem) in your path. (You might want to add a directory to your path just for these.) I would probably do this by:

    cd ~/bin.windows_exes
    ln -s /path/to/windows/executables/*.exe .
    prename 's/.exe$//' *.exe
    

    (prename is the perl version of rename. It's the rename I know and use.)

    Then adding to your path is via:

    PATH="$PATH":"$HOME"/bin.windows_exe
    

Edit: Apparently OP wanted a setup script. After it was rejected here (which I agreed with, it was a bit simplistic), OP posted it as his own answer and move the check there, so I wrote my own. I tried to conform to version 7 unix so it would be compatible with any Bourne shell derivative.

It includes options to set the output directory, the extension list, be verbose or quiet, to dryrun, and to remove the links. It is careful never to overwrite existing things, except that it will replace a broken link.

The extension list and default output directory at the top can edited, as can the list of default windows directories to link to at the bottom. (For the later, I assumed the MS-Windows drive was mounted at /windows.) You could consider adding any directory in your MS-Windows path to this list.

#!/bin/sh

exts='exe bat cmd com vbs vbe js jse wsf wsh msc'
output_directory="$HOME/.windows_binaries"

quiet=false
verbose=false
dryrun=false
remove=false
debug=false


usage() {
    echo "`basename "$0"`" '<options>' '[<windows_directories>]'
    echo '   -d dir        Specify the output directory'
    echo '   -e ext        Add a windows extension, like "exe"'
    echo '   -E            Clear the list of extensions'
    echo '   -v, --verbose Verbose (report normal changes)'
    echo '   -q, --quiet   Quiet (don'"'"'t report errors)'
    echo '   -n, --dryrun  Do not make any changes'
    echo '   --remove      Remove links that would otherwise be made'
    (
        echo 'If no windows directories are specified,'
        echo 'everything in the PATH is done implicitly.'
        echo 'For Cygwin'
        echo 'or Microsoft'"'"'s "Windows Subsystem for Linux",'
        echo 'it is assumed'
        echo 'that PATH has been translated to Unix conventions.'
    ) | fmt
    exit 2
}

add_link() {
    $debug && echo consider "$1" "$2"
    if test -h "$2" 
    then
        # here, the target already exists, and is a link
        oldlink="`readlink "$2"`"
        if test "$1" = "$oldlink"
        then
            if $remove
            then
                $verbose && echo remove "$2"
                $dryrun || rm "$2"
            fi
        else
            if $remove
            then
                :
            else
                if test ! -e "$2"
                then
                    # old link broken, replace it
                    $dryrun || rm "$2"
                    $dryrun || ln -s "$1" "$2"
                    $verbose && echo replace broken "$2" as "$1"
                else
                    $quiet || echo "$2" already links to "$oldlink" -- not changing it to "$1"
                fi
            fi
        fi
    elif $remove
    then
        :
    elif test -e "$2" 
    then
        # here, the target already exists
        $quiet || echo Not replacing file "$2"
    else
        # here, the target does not exist
        $dryrun || ln -s "$1" "$2" 
        $verbose && echo link "$2" as "$1"
    fi

}

add_directory() {
    dir="$1"
    case "$dir" in 
        */) dir="` expr "$dir" : '\(*\)/' `" ;;
    esac
    $debug && echo consider "$1"

    for ext in $exts
    do
        for path in "$dir"/*."$ext"
        do
            # wildcards in bourne shell always return something, even if it is just the wildcard
            if test -f "$path" 
            then
                fn=`basename "$path" ."$ext"`
                add_link "$path" "$output_directory"/"$fn"
            fi
        done
    done
}

## Can't use getopt because it doesn't handle spaces, and windows directories
## are notorious for having spaces.  Can't use getopts as it is too recent.

have_dirs=
mode=
for arg in "$@"
do
    case "$mode":"$arg" in
        :-d) mode=-d ;;
        :-d*) output_directory="`expr "$arg" : "-d\(*\)"`" ;;
        -d:*) output_directory="$arg" mode= ;;
        :-e) mode=-e ;;
        :-e*) exts="$exts `expr "$arg" : "-d\(*\)"`" ;;
        -e:*) exts="$exts $arg" mode= ;;
        :-E) exts="" ;;
        :-q) quiet=true ;;
        :--quiet) quiet=true ;;
        :-v) verbose=true ;;
        :--verbose) verbose=true ;;
        :-n) dryrun=true ;;
        :--dryrun) dryrun=true ;;
        :--remove) remove=true ;;
        :-*) echo Bad option "$arg" ; usage ;;
        :*)
            if test -d "$arg"
            then
                have_dirs=true
            else
                echo Argument "$arg" is not a directory 
                usage
            fi
            ;;
    esac
done

if test -z "$exts"
then
    echo No extensions specified '(and you cleared the list)'
    usage
fi

if test ! -d "$output_directory"
then
    if $remove
    then
        echo Nothing to do
        exit 0
    fi

    mkdir "$output_directory"
    $verbose && echo made directory "$output_directory"
fi

if test -n "$have_dirs"
then
    mode=
    for arg in "$@"
    do
        case "$mode":"$arg" in
            :-[de]) mode=$arg ;;
            :-[de]*) ;;
            -[de]:*) mode= ;;
            :-[Eqvn]) ;;
            :--quiet) ;;
            :--verbose) ;;
            :--dryrun) ;;
            :--remove) ;;
            :*) add_directory "$arg" ;;
        esac
    done

else
    # Do all the directories in the path.  
    IFS0="$IFS"
    IFS=:
    for pdir in $PATH
    do
        IFS="$IFS0"
        add_directory "$pdir"
    done
fi

$remove && rmdir "$output_directory" 2>/dev/null
$verbose && test ! -d "$output_directory" && echo remove directory "$output_directory"

Edit: revised script to use the PATH variable. It is implicit in OP's question that he has already got all the windows executable directories he wants to use in the path.


You can set up a command_not_found_handler. Untested:

function command_not_found_handler {
  for ext in ${(s:;:)${PATHEXT-".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.msc"}}; do
    if (( $+commands[$1$ext] )); then
      exec -- "$1$ext" "${@:2}"
    fi
  done
  print -ru2 "command not found: $1"
  return 127
}

Note that this is specifically if foo.exe (or other extension such as .bat) works but you want to be able to type just foo. If foo.exe doesn't work in zsh but foo works in cmd and invokes a program called foo.exe, you have a different problem.


Create an alias in your .zshrc.

alias docker='docker.exe' 

Afterwards, source it and you're good to go. I'd consider doing this only for the executables you require. After all, you might want to run the linux ping instead of ping.exe.