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

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

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

在此输入图像描述

explorer.exe .例如减少to explorer .、 or notepad.exe fileto notepad file、 or docker.exe pstodocker ps等调用。

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

编辑:

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

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

答案1

您可以设置一个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
}

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

答案2

你有三个选择。

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

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

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

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

    prenameperl的版本rename。这是rename我所知道和使用的。)

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

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

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

它包括设置输出目录、扩展列表、详细或安静、干运行以及删除链接的选项。小心不要覆盖现有的东西,除非它会替换损坏的链接。

顶部的扩展列表和默认输出目录可以编辑,底部链接的默认 Windows 目录列表也可以编辑。 (对于后者,我假设 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"

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

答案3

在您的 中创建一个别名.zshrc

alias docker='docker.exe' 

之后,source你就可以开始了。我会考虑只对您需要的可执行文件执行此操作。毕竟,您可能想运行 linuxping而不是ping.exe.

答案4

遵循@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

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

$HOME/.zshrc文件中:

export PATH=$PATH:$HOME/.windows_binaries

上述脚本的解释:

  • #!/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

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

相关内容