Windows 中 Julia 的 shell 模式与 `;dir` 报错

Julia 可以在 REPL 中执行 Shell 命令,就是先输入 ; 进入 shell 模式,然后输入命令就能执行对应的命令。这个操作在 macOS 和 Linux 下都没什么问题。就是 windows 下大家就会出现奇怪的错误。

以下演示均在 Julia 1.0.5(LTS)版本中运行。操作系统为 Windows 10。

shell 模式

图中提示符为绿色的 julia> 时为正常的 REPL 模式,提示符变成红色的 shell> 时则进入了 shell 模式。

一些命令,如图中的 whoami 能正常执行并返回;另一些,如 dir 则报错

ERROR: IOError: could not spawn `dir`: no such file or directory (ENOENT)

中文社区中相关的讨论


下面简单说明一下这个问题的原因以及解决办法(workaround)。

发生错误的原因

julia 的 shell 模式不是在执行命令,而是在调用外部程序。

命令从不会在 shell 中运行。相反地,Julia 会直接解析命令语法,适当地插入变量并像 shell 那样拆分单词,同时遵从 shell 的引用语法。命令会作为 julia 的直接子进程运行,使用 forkexec 调用。
—— 运行外部程序 · Julia中文文档

前面使用的 dir 是 cmd 的内部指令(Internal commands),这种命令会被 cmd 捕获,然后执行相应的操作,并没有一个程序叫做 dir.exe 放在硬盘上,如果有那一定可以成功调用。

与之相对的外部命令(External commands)有对应的 exe 文件可供调用,所以能在 shell 模式中成功执行。cmd 外部命令的 exe 文件通常放在 C:\WINDOWS\System32 文件夹中,这个文件夹是 PATH 环境变量的一部分,所以无需输入完整路径即可执行命令。

内部命令列表

ref: Internal commands | SS64.comAn A-Z Index of Windows CMD commands | SS64.com

ASSOC, 
BREAK, 
CALL ,CD/CHDIR, CLS, COLOR, COPY, 
DATE, DEL, DIR, DPATH, 
ECHO, ENDLOCAL, ERASE, EXIT, 
FOR, FTYPE, 
GOTO, 
IF, 
KEYS, 
MD/MKDIR, MKLINK (vista and above), MOVE, 
PATH, PAUSE, POPD, PROMPT, PUSHD, 
REM, REN/RENAME, RD/RMDIR, 
SET, SETLOCAL, SHIFT, START, 
TIME, TITLE, TYPE, 
VER, VERIFY, VOL

解决办法

tl; dr / 太长不看

  • ;cmd /c dir。显式的让 cmd.exe 执行所需的命令。
  • readdir() # Base.Filesystem 使用内置函数。文件系统 · Julia中文文档
  • 在 git bash(MSYS2)中启动 Julia。(WSL 目前不行)

调用 cmd.exe

ref: #33924

内部命令不能直接在 shell 模式中使用,但可以通过 cmd.exe 中转间接使用。

简单粗暴的方式是调用 cmd.exe,然后把要执行的命令当作参数传递给他(cmd /c),只要小心转义相关的问题,这个方法还是比较稳妥的。

例子
;cmd /c dir

使用 Base.Filesystem 完成所需的操作

Base.Filesystem 将一些命令行常用操作封装成了 julia 的函数,如果是执行一些简单的操作,直接调用函数就好了。

例子

Base.Filesystem 中的函数无需导入即可使用

julia> pwd()
"C:\\Users\\woclass\\AppData\\Local\\Julia-1.0.5"

julia> readdir() # 等价于 dir
8-element Array{String,1}:
 "bin"
 "etc"
 "include"
 "julia.lnk"
 "lib"
 "LICENSE.md"
 "share"
 "Uninstall.exe"

Base.Filesystem 中有哪些函数可以看文档 文件系统 · Julia中文文档
或者参考以下列表

以下输出通过在 REPL 中输入 Base.Filesystem.(最后有一个点),然后按两次 tab 键触发自动补全而得到。
输出中全小写的才是函数,其他的是一些宏、类型和常量

julia> VERSION
v"1.0.5"

julia> Base.Filesystem.
@stat_call             S_IRWXO                 eval                    longpath                relpath
AbstractFile           S_IRWXU                 expanduser              lstat                   rename
File                   S_IWGRP                 filemode                mkdir                   rm
JL_DUMMY               S_IWOTH                 filesize                mkpath                  samefile
JL_O_APPEND            S_IWUSR                 futime                  mktemp                  sendfile
JL_O_CREAT             S_IXGRP                 gperm                   mktempdir               splitdir
JL_O_EXCL              S_IXOTH                 homedir                 mtime                   splitdrive
JL_O_NOCTTY            S_IXUSR                 include                 mv                      splitext
JL_O_RANDOM            StatStruct              isabspath               normpath                stat
JL_O_RDONLY            UV_FS_SYMLINK_JUNCTION  isblockdev              open                    symlink
JL_O_RDWR              _win_tempname           ischardev               operm                   temp_prefix
JL_O_SEQUENTIAL        abspath                 isdir                   path_absolute_re        tempdir
JL_O_SHORT_LIVED       basename                isdirpath               path_dir_splitter       tempname
JL_O_TEMPORARY         cd                      isfifo                  path_directory_re       touch
JL_O_TRUNC             checkfor_mv_cp_cptree   isfile                  path_ext_splitter       truncate
JL_O_WRONLY            checkmode               islink                  path_separator          unlink
SEEK_CUR               chmod                   ismount                 path_separator_re       uperm
SEEK_END               chown                   ispath                  pathsep                 uv_dirent_t
SEEK_SET               contractuser            issetgid                pwd                     walkdir
S_IRGRP                cp                      issetuid                readbytes!              write
S_IROTH                cptree                  issocket                readdir
S_IRUSR                ctime                   issticky                readlink
S_IRWXG                dirname                 joinpath                realpath

在 git bash 中启动 julia

All of this weirdness on Windows could be addressed by bundling git bash with Julia to handle the shell mode. —— PetrKryslUCSD #23597

git bash 提供了一个 MSYS2 的环境。

MSYS2 是用于辅助 Windows 版 MinGW 进行命令行开发的配套软件包,提供了部分 Unix 工具。

所以它既支持 windows 的 dir 也支持 posix 标准的 ls。使用它启动 julia,shell 就变成了 git bash 这样就减少了很多的问题。
如果安装了 Git for Windows+git bash 这样用 julia 还是很方便的。

git bash

WSL

失败的尝试。

按照类似 git bash 的思路我试了下 wsl,发现不仅 dir 不能用,lspwd 都不能用,cd 倒是能用。
原因不明。但在 wsl 中这些命令都是能够正常使用的。


参考资料&相关的链接

外部链接

cmd 命令列表

相关的 issue

相关的 pr

英文 discourse 论坛

Stack Overflow

京ICP备17009874号-2