如何用 scala 调用外部命令、重定向及使用管道?

太长不看版

在 scala 中调用外部命令行程序非常简单,只需要导入 sys.process 包,并在需要调用的命令行外加双引号,并在行尾加 ! 号,如果想获得标准输出,就在行尾加 !! 号,如果需要管道或者重定向,请使用 #| 或者 #> 操作符,并注意在 #> 后面不能直接加文件名,需要使用 java.io.File 对象(关于如何快速运行 scala 交互式编程环境,请参考 如何利用 sbt 运行 Scala REPL )。举例:

// 导入需要的依赖
import sys.process._
import java.io.File

// 非阻塞式,使用 run 方法立即返回 scala.sys.process.Process 对象,并且外部命令同时执行
val process: Process = "ls -la".run

// 执行 ls -la 并且获取 exit code
val exitcode = "ls -la" !

// 执行 ls -la 并且获取标准输出
val stdout = "ls -la" !!

// 执行 ls -la . no_such_file.txt 并同时获取 exitcode,标准输出和标准错误
val n, e = new StringBuilder
val logger = ProcessLogger( l => n.append(l).append("\n"), l => e.append(l).append("\n"))
val exitcode = "ls -la . no_such_file.txt" ! logger

// bash 管道操作
val m = "ls -la" #| "grep foo" !!

// 输出重定向
"ls -la" #> new File("ls_output.txt") !

正文

我们在编写程序时经常会遇到需要在程序中调用外部命令的情况,scala 语言也提供了让程序员调用外部命令的功能。本文不但介绍如何在 scala 中调用外部命令行(比如 Linux 下的 bash 命令),也会讲解如何获取外部命令的标准输出、错误输出以及如何使用管道和重定向。关于如何快速运行 scala 交互式编程环境,请参考 如何利用 sbt 运行 Scala REPL

1.1 非阻塞式调用,获取 scala.sys.process.Process 对象

首先,导入 sys.process 包

scala> import sys.process._
import sys.process._

使用 run 方法获取进程对象

scala> val process: Process = "ls -la".run
process: scala.sys.process.Process = scala.sys.process.ProcessImpl$SimpleProcess@714975fd
total 24
drwxr-xr-x    7 kongl  staff    224 12 10 12:11 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
drwxr-xr-x    3 kongl  staff     96 12 10 12:11 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target

1.2 在 scala 中调用外部命令并获取 exit code

首先,导入 sys.process 包

scala> import sys.process._
import sys.process._

在需要调用的命令行外加双引号,并在行尾加 ! 号

scala> val exitcode = "ls -al" !
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
total 24
drwxr-xr-x    7 kongl  staff    224 12 10 12:11 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
drwxr-xr-x    3 kongl  staff     96 12 10 12:11 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target
exitcode: Int = 0

scala> print(exitcode)
0

2. 在 scala 中调用外部命令并获取标准输出和错误输出

2.1 只需要获取标准输出的情形

如果只需要标准输出,那么非常简单,只需要把上个例子中的 ! 替换为 !! 即可,例如

scala> import sys.process._
import sys.process._

scala> val stdout = "ls -la" !!
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
stdout: String =
"total 24
drwxr-xr-x    7 kongl  staff    224 12 10 12:23 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
drwxr-xr-x    4 kongl  staff    128 12 10 12:31 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target
"

scala> print(stdout)
total 24
drwxr-xr-x    7 kongl  staff    224 12 10 12:23 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
drwxr-xr-x    4 kongl  staff    128 12 10 12:31 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target

2.2 需要同时获取标准输出和错误输出的情形

如果需要同时获取标准输出和错误输出,那我们需要用到 ProcessLogger 类,例如

scala> import sys.process._
import sys.process._

scala> val n, e = new StringBuilder
n: StringBuilder =
e: StringBuilder =

scala> val logger = ProcessLogger( l => n.append(l).append("\n"), l => e.append(l).append("\n"))
logger: scala.sys.process.ProcessLogger = scala.sys.process.ProcessLogger$$anon$1@25968d9c

scala> val exitcode = "ls -la . no_such_file.txt" ! logger
exitcode: Int = 1

scala> print(exitcode)
1
scala> print(n)
.:
total 24
drwxr-xr-x    7 kongl  staff    224 12 10 12:23 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
drwxr-xr-x    4 kongl  staff    128 12 10 12:31 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target

scala> print(e)
ls: no_such_file.txt: No such file or directory

3. 使用管道和重定向

3.1 如何使用管道

在 scala 中调用 shell 命令,我们不能直接在命令行中使用管道,这是因为 scala 在调用命令时并不会启动 bash 环境,而管道和重定向是 bash 支持的功能。例如,直接运行 “ls -la | grep foo” 我们得到如下报错信息:

scala> import sys.process._
import sys.process._

scala> "ls -la | grep foo" !!
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
ls: foo: No such file or directory
ls: grep: No such file or directory
ls: |: No such file or directory
java.lang.RuntimeException: Nonzero exit value: 1
  at scala.sys.package$.error(package.scala:30)
  at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.slurp(ProcessBuilderImpl.scala:138)
  at scala.sys.process.ProcessBuilderImpl$AbstractBuilder.$bang$bang(ProcessBuilderImpl.scala:108)
  ... 36 elided

因此,我们需要使用 scala 语言提供的 #| 操作符,例如:

scala> val m = "ls -la" #| "grep foo" !!
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
m: String =
"-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
"

scala> print(m)
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt

也可以多个 #! 连用,例如

scala> val m = "ls -la" #| "grep txt" #| "wc -l" !!
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
m: String =
"       3
"

scala> print(m)
       3

3.2 如何使用重定向

和使用管道类似,scala 为标准输出重定向提供了 #> 操作符,需要注意的是,在 #> 后面不能直接加文件名,需要使用 File 对象,例如:

scala> import sys.process._
import sys.process._

scala> import java.io.File
import java.io.File

scala> "ls -la" #> new File("ls_output.txt") !
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
res31: Int = 0

scala> "cat ls_output.txt" !!
warning: there was one feature warning; for details, enable `:setting -feature' or `:replay -feature'
res32: String =
"total 24
drwxr-xr-x    8 kongl  staff    256 12 10 15:28 .
drwx------@ 458 kongl  staff  14656 12 10 12:11 ..
-rw-r--r--    1 kongl  staff      9 12 10 12:11 and_more.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 bar.txt
-rw-r--r--    1 kongl  staff     12 12 10 12:11 foo.txt
-rw-r--r--    1 kongl  staff      0 12 10 15:28 ls_output.txt
drwxr-xr-x    4 kongl  staff    128 12 10 12:31 project
drwxr-xr-x    5 kongl  staff    160 12 10 12:17 target
"

3.3 其他 bash 操作符对应

#<   输入重定向
#>   输出重定向
#>>  追加输出重定向

#&&  和
#!!  或

参考链接

  1. How to execute (exec) external system commands in Scala
  2. Scala: How to handle the STDOUT and STDERR of external commands
  3. ProcessLogger
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值