如何在没有明确指定 .exe 扩展名的情况下从终端运行 Windows 可执行文件?

Ani*_*ahu 7 shell zsh windows oh-my-zsh windows-subsystem-for-linux

正如标题所说的大部分问题,我们如何在不指定.exe末尾显式后缀的情况下运行 Windows 可执行文件。

在此处输入图片说明

例如减少像呼叫explorer.exe .explorer .,或notepad.exe filenotepad file,或docker.exe psdocker ps

我想知道是否有本地方法可以这样做,或者是否有一种方法可以处理command not found错误并重定向到.exe可执行文件,如果$Path命令名称中没有可用的 linux 程序。

编辑:

之前忘记加环境了,这里是:

  • 终端:Windows 终端
  • Linux:WSL 上的 Ubuntu 20.04(适用于 Linux 的 Windows 子系统)
  • 外壳:zsh
  • 操作系统:Windows 10 1909

Dav*_* G. 5

你有三个选择。

  1. 始终键入.exe. 命令完成可能会有所帮助,假设您的 shell 有它。但这是你试图避免的。

  2. 其他人已经指出的别名解决方案。

  3. 建立不带 .exe 的符号链接到 Windows 可执行文件。这些应该放在您路径中的一个目录中(在 Unix/Linux 文件系统上)。(您可能只想为这些在路径中添加一个目录。)我可能会这样做:

    cd ~/bin.windows_exes
    ln -s /path/to/windows/executables/*.exe .
    prename 's/.exe$//' *.exe
    
    Run Code Online (Sandbox Code Playgroud)

    prename是 的perl版本rename。这是rename我知道和使用的。)

    然后添加到您的路径是通过:

    PATH="$PATH":"$HOME"/bin.windows_exe
    
    Run Code Online (Sandbox Code Playgroud)

编辑:显然 OP 想要一个安装脚本。在这里被拒绝后(我同意,这有点简单),OP 将其发布为他自己的答案并将支票移到那里,所以我写了自己的答案。我试图符合版本 7 unix,因此它可以与任何 Bourne shell 衍生产品兼容。

它包括设置输出目录、扩展列表、详细或安静、dryrun 和删除链接的选项。永远不要覆盖现有的东西,除非它会替换损坏的链接。

顶部的扩展列表和默认输出目录可以编辑,底部的默认窗口目录列表也可以编辑。(对于后者,我假设 MS-Windows 驱动器安装在/windows.)您可以考虑将 MS-Windows 路径中的任何目录添加到此列表中。

#!/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"
Run Code Online (Sandbox Code Playgroud)

编辑:修改脚本以使用 PATH 变量。OP 的问题隐含着他已经获得了他想要在路径中使用的所有 Windows 可执行目录。


Gil*_*il' 5

您可以设置一个command_not_found_handler. 未经测试:

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
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果foo.exe(或其他扩展名,例如.bat)有效,但您希望能够仅键入foo. 如果foo.exe在 zsh 中不起作用但foo在 cmd 中工作并调用一个名为 的程序foo.exe,则您会遇到不同的问题。


Ani*_*ahu 1

遵循@DavidG 给出的想法。我做了这个,希望这会让某人开心:)

以下脚本将为 Windows 中的所有可执行文件创建缺失的目录和符号链接(如果它不存在):

#!/usr/bin/zsh

mkdir -p $HOME/.windows_binaries
cd $HOME/.windows_binaries

for ext in $( echo ${PATHEXT-".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.msc"} | tr \; ' ' ); do
  for command in ${(M)commands:#*$ext}; do
    if [ -f $(basename $command $ext) ]; then; else
      ln -s $command $(basename $command $ext)
    fi
  done
done
Run Code Online (Sandbox Code Playgroud)

然后我们可以将~/.windows_binaries文件夹附加到路径中,这样如果没有找到 linux 可执行文件,将触发 windows 可执行文件:

$HOME/.zshrc文件中:

export PATH=$PATH:$HOME/.windows_binaries
Run Code Online (Sandbox Code Playgroud)

上述脚本的解释:

  • #!/usr/bin/zsh- Shebang 将重定向要由 zsh 执行的脚本,因为我们确实使用了 bash 默认情况下不可用的过滤命令。
  • mkdir -p $HOME/.windows_binaries- 如果路径不存在,则创建它:)
  • cd $HOME/.windows_binaries- 将cwd更改为它
  • for ext in $(...); do- 循环遍历窗口中被视为脚本的每个扩展
  • for command in ${(M)commands:#*$ext}; do-$commands索引当前 shell 中可以使用的所有命令,并使用 zsh 过滤器过滤以$ext扩展名结尾的命令。
  • if [ -f $(basename $command $ext) ]; then; else- 如果已创建不带扩展名的命令的符号链接,则跳过,否则继续创建带有ln -s.

编辑:

感谢@user414777帮助优化脚本。现在它的速度提高了大约 2-3 倍(7-9 秒):

#!/usr/bin/zsh

mkdir -p $HOME/.windows_binaries
cd $HOME/.windows_binaries

all_exts=${${${PATHEXT-".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.msc"}//;/|}//./}
IFS=$'\n'

if [ $ZSH_VERSION ]; then setopt sh_word_split; fi

for command in $(whence -psm "*.($all_exts)"); do
  sym_name=${${command##*/}%.*}
  if [ -f $sym_name ]; then; else
    ln -s $command $sym_name
  fi
done
Run Code Online (Sandbox Code Playgroud)

我还了解到,正则表达式替换/字符串更改的 shell 替换比将其管道传输到诸如tr. 而且IFS并不是直接在zsh中直接使用来扩展替换的,我们必须事先配置它。