正如标题所说的大部分问题,我们如何.exe
在末尾不指定显式后缀的情况下运行 Windows 可执行文件。
explorer.exe .
例如减少to explorer .
、 or notepad.exe file
to notepad file
、 or docker.exe ps
todocker 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
你有三个选择。
始终键入
.exe
.假设您的 shell 有命令完成功能,它可能会有所帮助。但这正是您想要避免的。其他人已经指出了别名解决方案。
建立不带 .exe 的符号链接到 Windows 可执行文件。这些应该放置在您的路径中的目录(在 Unix/Linux 文件系统上)中。 (您可能只想为这些目录添加一个目录到您的路径中。)我可能会通过以下方式执行此操作:
cd ~/bin.windows_exes ln -s /path/to/windows/executables/*.exe . prename 's/.exe$//' *.exe
(
prename
是perl
的版本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中直接使用来扩展替换的,我们必须事先配置它。