现在位置: 首页 > Julia 教程 > 正文

Julia 运行外部程序

Julia 借鉴 Shell、Perl 和 Ruby 的反引号(backtick)语法来运行外部命令。命令用反引号 `...` 包围:

实例

julia> mycommand = `echo hello`
`echo hello`

julia> typeof(mycommand)
Cmd

注意:与 Shell 不同,反引号本身不会立即执行命令,而是创建一个 Cmd 对象。Julia 运行命令时不经过 Shell,而是直接 fork/exec 子进程,因此没有通配符展开、管道符等 Shell 特性,也不会有引号转义问题。


运行命令:run

run() 函数执行命令,输出直接打印到终端;命令失败会抛出 ProcessFailedException:

实例

julia> run(`echo hello`);
hello

# 命令失败时抛异常
julia> run(`false`)
ERROR: failed process: Process(`false`, ProcessExited(1)) [1]

捕获命令输出

read() 可以捕获命令的输出,readchomp() 还会去掉结尾的换行:

实例

julia> read(`echo hello`, String)
"hello\n"

julia> readchomp(`echo hello`)
"hello"

# 实际应用:获取文件数量
julia> parse(Int, readchomp(`ls | wc -l`))

插值

反引号命令同样支持 $ 插值,Julia 会自动处理好参数引号和转义,不用担心特殊字符问题:

实例

julia> file = "/Volumes/External HD/data.csv";

# 路径带空格也安全,Julia 自动把它当单个参数处理
julia> `sort $file`
`sort '/Volumes/External HD/data.csv'`

# 插值数组会展开成多个参数
julia> files = ["/etc/passwd", "/data.csv"];

julia> `grep foo $files`
`grep foo /etc/passwd /data.csv`

管道:pipeline

Shell 的 |&> 在反引号里需要转义,构造管道要用 pipeline() 函数:

pipeline 子进程管道示意图

实例

# 把 echo 的输出通过管道传给 sort
julia> run(pipeline(`echo hello`, `sort`));
hello

更复杂的例子:统计当前目录下文件数量:

实例

julia> readchomp(pipeline(`ls`, `wc -l`))
"12"

# 重定向输出到文件
julia> run(pipeline(`echo runoob`, "out.txt"))
Process(`echo runoob`, ProcessExited(0))

# 两个命令并行执行,输出合并
julia> run(pipeline(`echo world` & `echo hello`, `sort`));
hello
world

注意:Windows 系统中 echo、dir 等是 cmd.exe 的内建命令而不是外部程序,需要写成 cmd /C echo hello 才能运行。Julia 官方建议优先用纯 Julia 实现相同功能。


Cmd 对象的选项

可以用 Cmd 构造函数给命令指定工作目录、环境变量等选项:

实例

# dir 选项指定工作目录
julia> run(Cmd(`pwd`, dir="/"));
/

# env 选项设置环境变量
julia> run(Cmd(`sh -c "echo foo \$HOWLONG"`, env=("HOWLONG" => "ever!",)));
foo ever!

# ignorestatus 忽略命令的退出码(不因失败而抛异常)
julia> run(Cmd(Cmd(["false"]), ignorestatus=true))
Process(`false`, ProcessExited(1))

setenvaddenv 提供了替换或追加环境变量的另一种方式:

实例

julia> run(addenv(`sh -c "echo foo \$HOWLONG"`, "HOWLONG" => "ever!"));
foo ever!

Julia 与 Shell 的取舍

很多 Shell 能做的事 Julia 自己就能做,官方建议优先用纯 Julia 写法:

Shell 写法Julia 替代写法
ls *.txt(通配符)filter(endswith(".txt"), readdir()) 或 Glob.jl 包
cat a.txtread("a.txt", String)
rm file.txtrm("file.txt")
mkdir dirmkdir("dir")
command1 | command2读出前者输出再传给后者,或 pipeline

避免默认走 Shell 的原因:进程启动慢、特殊字符转义脆弱、错误处理差、跨平台性差。Julia 直接执行子进程,把这些坑都绕开了。