Julia 运行外部程序
Julia 借鉴 Shell、Perl 和 Ruby 的反引号(backtick)语法来运行外部命令。命令用反引号 `...` 包围:
实例
julia> mycommand = `echo hello`
`echo hello`
julia> typeof(mycommand)
Cmd
`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]
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`))
"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`
# 路径带空格也安全,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() 函数:
实例
# 把 echo 的输出通过管道传给 sort
julia> run(pipeline(`echo hello`, `sort`));
hello
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
"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))
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))
setenv 和 addenv 提供了替换或追加环境变量的另一种方式:
实例
julia> run(addenv(`sh -c "echo foo \$HOWLONG"`, "HOWLONG" => "ever!"));
foo ever!
foo ever!
Julia 与 Shell 的取舍
很多 Shell 能做的事 Julia 自己就能做,官方建议优先用纯 Julia 写法:
| Shell 写法 | Julia 替代写法 |
|---|---|
ls *.txt(通配符) | filter(endswith(".txt"), readdir()) 或 Glob.jl 包 |
cat a.txt | read("a.txt", String) |
rm file.txt | rm("file.txt") |
mkdir dir | mkdir("dir") |
command1 | command2 | 读出前者输出再传给后者,或 pipeline |
避免默认走 Shell 的原因:进程启动慢、特殊字符转义脆弱、错误处理差、跨平台性差。Julia 直接执行子进程,把这些坑都绕开了。
