文档章节

Scala入门到精通——第三十节 Scala脚本编程与结束语

阿里云云栖社区
 阿里云云栖社区
发布于 06/26 17:42
字数 2816
阅读 7
收藏 0

摘要: 本节主要内容 REPL命令行高级使用 使用Scala进行Linux脚本编程 结束语 1. REPL命令行高级使用 在使用REPL命令行时,有时候我们需要粘贴的代码比较大,而普通的粘贴可能会些一些问题,比如中文粘贴会出现乱码、多行代码粘贴时会出错,此时需要用到REPL的高级功能。在日常开发过程中,我们粘贴多行代码的时候会遇到下列问题: //本意是要粘贴下面两行代码

本节主要内容

  1. REPL命令行高级使用
  2. 使用Scala进行Linux脚本编程
  3. 结束语

1. REPL命令行高级使用

在使用REPL命令行时,有时候我们需要粘贴的代码比较大,而普通的粘贴可能会些一些问题,比如中文粘贴会出现乱码、多行代码粘贴时会出错,此时需要用到REPL的高级功能。在日常开发过程中,我们粘贴多行代码的时候会遇到下列问题:

//本意是要粘贴下面两行代码
class Person(val name:String,val age:Int)
val p=new Person("摇摆少年梦",27)

//直接在REPL命令行粘贴的话,会出现下面情况
//1 不会一次性粘入,而是分作两行
//2 中文出现乱码
scala> class Person(val name:String,val age:Int)
defined class Person

scala> val p=new Person("??????????",27)
p: Person = Person@cf528

而对于一些长串跨行的代码,可能会出现报错,例如:

//本意是要粘贴下面的代码
if(p.age>10) 
    true
  else
    false

//但实际情况是这样的   
scala> if(p.age>10)
     |     true
res0: AnyVal = true

scala>   else
<console>:1: error: illegal start of definition
         else
         ^

scala>     false

那要怎么办呢?在REPL命令行中执行下列命令:

scala> :paste
// Entering paste mode (ctrl-D to finish)

if(p.age>10)
    true
  else
    false

// Exiting paste mode, now interpreting.

res3: Boolean = true

先输入:paste,然后按ctr+v键,可以正常粘贴内容,中文也不会出现乱码了:

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Person(val name:String,val age:Int)
val p=new Person("摇摆少年梦",27)

// Exiting paste mode, now interpreting.

defined class Person
p: Person = Person@1924b38

另外,在实际开发过程中,有些人会认为这种处理方式非常繁琐,Scala的创建者也为我们考虑过这个问题了,我们也可以在scala IDE for eclipse (在Intellij IDEA 中也有这个功能) 里面利用REPL命令行,使用方式是创建scala worksheet,创建方式如下: 
1 点击相应的包,然后右键,在new菜单中选择 scala worksheet 

2 在文件中输入相应的scala语句,worksheet会自动打印出相应的结果 

但是worksheet对中文的支持很不友好,例如下面的代码:

case class Person(val name:String,val age:Int)

object ScalaREPL {
  println("Scala worksheet")                      //> Scala worksheet
  val p=new Person("摇摆少年梦",27)                    //> p  : cn.scala.xtwy.jdbc.Person = Person(鎽囨憜灏戝勾姊�27)
}

worksheet最终得到的中文是乱码,因此在实际进行语言特性测试的时候尽量避免中文

scala中还有很多我们实际中没有接触过的命令,可以用 :help命令查看REPL现在支持的所有命令:

scala> :help
All commands can be abbreviated, e.g. :he instead of :help.
Those marked with a * have more detailed help, e.g. :help imports.

:cp <path>                 add a jar or directory to the classpath
:help [command]            print this summary or command-specific help
:history [num]             show the history (optional num is commands to show)
:h? <string>               search the history
:imports [name name ...]   show import history, identifying sources of names
:implicits [-v]            show the implicits in scope
:javap <path|class>        disassemble a file or class name
:load <path>               load and interpret a Scala file
:paste                     enter paste mode: all input up to ctrl-D compiled tog
ether
:power                     enable power user mode
:quit                      exit the interpreter
:replay                    reset execution and replay all previous commands
:reset                     reset the repl to its initial state, forgetting all s
ession entries
:sh <command line>         run a shell command (result is implicitly => List[Str
ing])
:silent                    disable/enable automatic printing of results
:type [-v] <expr>          display the type of an expression without evaluating
it
:warnings                  show the suppressed warnings from the most recent lin
e which had any

2. 使用Scala进行Linux脚本编程

本节Linux脚本内容大部分来源于scala cookbook,部分经过本人修改以在Ubuntu Linux上进行演示。

我们在第一节中提到,Scala不仅仅可以进行大规模分布式应用程序开发(例如Spark内存计算框架),也可以进行服务器端脚本编程即它可以替代Linux中的shell (Bourne Shell, Bash)或其它如 Perl, PHP, Ruby等可用于服务器端脚本编程的语言。下面给出的是一个简单示例(前提是要有linux操作系统,本节所有示例都是在ubuntu Linux下运行的):

#!/bin/sh
exec scala "$0" "$@"
!#
println("HellO,Linux World")

将上面的内容保存为hello.sh文件,然后用下列命令增加其执行权限:

root@sparkmaster:/home/zhouzhihu/scalaLearning# chmod +x hello.sh 
root@sparkmaster:/home/zhouzhihu/scalaLearning# ./hello.sh 
HellO,Linux World

可以看到我们第一个服务器脚本已经运行成功。前面的代码中,#!符号表示的是Unix shell脚本的开始,它会调用Unix Bourne shell。exce命令是内置的shell,表示需要执行scala 命令,其中0绑定的是hello.sh脚本名称,@ 绑定的是我们输入的参数。!#表示脚本声明头部的结束。在脚本中可以使用任何的scala语法,例如:

#!/bin/sh
exec scala "$0" "$@"
!#

class Person(val firstName:String,val secondName:String){
  override toString()="firstName:"+firstName+",secondName:"+secondName
}

println(new Person("john","Barake"))

上述代码执行结果:

root@sparkmaster:/home/zhouzhihu/scalaLearning# ./person.sh 
firstName:john,secondName:Barake

除此之外,我们还可以定义应用程序对象,可以扩展自App,也可以实现自己的Main方法,例如:

#!/bin/sh
exec scala "$0" "$@"
!#
object Hello extends App {
println("Hello Ubuntu Linux 10.04")
//如果后面带参数的话,可以捕获所有的参数
args.foreach(println)
}
Hello.main(args)

下面给出的是不带参数的执行结果:

root@sparkmaster:/home/zhouzhihu/scalaLearning# ./HelloApp.sh
Hello Ubuntu Linux 10.04

下面给出的是带参数的执行结果,如:

root@sparkmaster:/home/zhouzhihu/scalaLearning# ./HelloApp.sh hello xuetuwuyou

Hello Ubuntu Linux 10.04
hello
xuetuwuyou

当然,还可以实现自己的main方法,如:

#!/bin/sh
exec scala "$0" "$@"
!#
object Hello {
  def main(args: Array[String]) {
    println("Hello, world")
    args.foreach(println)
  }
}
Hello.main(args)

同extends App是一样的。

如果脚本中需要应用到第三方库的话,可以采用下列方式进行包引入:

#!/bin/sh
exec scala -classpath "lib/slick_2.11_2.1.0.jar:lib/mysql-connector-java-5.1.18-bin.jar" "$0" "$@"
!#

import scala.slick.driver.MySQLDriver.simple._

object CoffeeExample extends App {

  class Suppliers(tag: Tag) extends Table[(Int, String, String, String, String, String)](tag, "SUPPLIERS") {
    def id = column[Int]("SUP_ID", O.PrimaryKey) // This is the primary key column
    def name = column[String]("SUP_NAME")
    def street = column[String]("STREET")
    def city = column[String]("CITY")
    def state = column[String]("STATE")
    def zip = column[String]("ZIP")
    // Every table needs a * projection with the same type as the table's type parameter
    def * = (id, name, street, city, state, zip)
  }
  val suppliers = TableQuery[Suppliers]

  // Definition of the COFFEES table
  class Coffees(tag: Tag) extends Table[(String, Int, Double, Int, Int)](tag, "COFFEES") {
    def name = column[String]("COF_NAME", O.PrimaryKey)
    def supID = column[Int]("SUP_ID")
    def price = column[Double]("PRICE")
    def sales = column[Int]("SALES")
    def total = column[Int]("TOTAL")
    def * = (name, supID, price, sales, total)
    // A reified foreign key relation that can be navigated to create a join
    def supplier = foreignKey("SUP_FK", supID, suppliers)(_.id)
  }
  val coffees = TableQuery[Coffees]

  Database.forURL("jdbc:mysql://localhost:3306/slick", "root", "123",
    driver = "com.mysql.jdbc.Driver") withSession {

      implicit session =>
        // Create the tables, including primary and foreign keys
        (suppliers.ddl ++ coffees.ddl).create

        // Insert some suppliers
        suppliers += (101, "Acme, Inc.", "99 Market Street", "Groundsville", "CA", "95199")
        suppliers += (49, "Superior Coffee", "1 Party Place", "Mendocino", "CA", "95460")
        suppliers += (150, "The High Ground", "100 Coffee Lane", "Meadows", "CA", "93966")

        // Insert some coffees (using JDBC's batch insert feature, if supported by the DB)
        coffees ++= Seq(
          ("Colombian", 101, 7.99, 0, 0),
          ("French_Roast", 49, 8.99, 0, 0),
          ("Espresso", 150, 9.99, 0, 0),
          ("Colombian_Decaf", 101, 8.99, 0, 0),
          ("French_Roast_Decaf", 49, 9.99, 0, 0))

        coffees foreach {
          case (name, supID, price, sales, total) =>
            println("  " + name + "\t" + supID + "\t" + price + "\t" + sales + "\t" + total)

        }
        val q1 = for (c <- coffees)
          yield LiteralColumn("  ") ++ c.name ++ "\t" ++ c.supID.asColumnOf[String] ++
          "\t" ++ c.price.asColumnOf[String] ++ "\t" ++ c.sales.asColumnOf[String] ++
          "\t" ++ c.total.asColumnOf[String]
        // The first string constant needs to be lifted manually to a LiteralColumn
        // so that the proper ++ operator is found
        q1 foreach println

        // Perform a join to retrieve coffee names and supplier names for
        // all coffees costing less than $9.00
        val q2 = for {
          c <- coffees if c.price < 9.0
          s <- suppliers if s.id === c.supID
        } yield (c.name, s.name)

    }

}
//这点与一般的应用程序不同
CoffeeExample.main(args)

通过上述代码不难发现,脚本编程与一般的Scala应用程序开发有着非常多的相似之处,不同之处仅在于在脚本编程需要加入下面这样的样板代码

#!/bin/sh  //样板代码
exec scala -classpath "lib/slick_2.11_2.1.0.jar:lib/mysql-connector-java-5.1.18-bin.jar" "$0" "$@"
!# //样板代码

....................

CoffeeExample.main(args) //样板代码

有时候,我们也需要对命令行参数进行捕获(例如判断命令行的个数或输入的参数类型等),然后进行相应的操作,前面已经演示了如何打印输出命令行参数,这里我们更多实际中可能会遇到的一些经典案例: 
1 判断输入参数的个数,不满足要求则给出提示

#!/bin/sh
exec scala "$0" "$@"
!#
if (args.length != 2) {
    Console.err.println("Usage: replacer <search> <replace>")
    System.exit(1)
}
val searchPattern = args(0)
val replacePattern = args(1)
println(s"Replacing $searchPattern with $replacePattern ...")

执行结果如下:

root@sparkmaster:/home/zhouzhihu/scalaLearning# ./argsNumberDemo.sh xuetuwu xuetuwuyou
Replacing xuetuwu with xuetuwuyou ...
root@sparkmaster:/home/zhouzhihu/scalaLearning# ./argsNumberDemo.sh
Usage: replacer <search> <replace>

2 交互式命令行,提示用户输入

#!/bin/sh
exec scala "$0" "$@"
!#
// write some text out to the user with Console.println
Console.println("Hello")
// Console is imported by default, so it's not really needed, just use println
println("World")
// readLine lets you prompt the user and read their input as a String
val name = readLine("What's your name? ")
// readInt lets you read an Int, but you have to prompt the user manually
print("How old are you? ")
val age = readInt()
// you can also print output with printf
println(s"Your name is $name and you are $age years old.")

下面给出的是其执行结果:

root@sparkmaster:/home/zhouzhihu/scalaLearning# ./promptDemo.sh 
Hello
World
What's your name? yaobaishaonianmeng
How old are you? 27
Your name is yaobaishaonianmeng and you are 27 years old.

3 加速代码的执行: 
scala脚本在执行的过程中,也是通过编译、执行的步骤来进行的,有时候为加速脚本的执行,意图是将编译后的脚本保存下来,在执行时候如果脚本创建之后没有发生变化的话,则直接使用以前编译好的脚本。实现方式是在脚本声明的时候用-savecompiled。

#!/bin/sh
exec scala -savecompiled "$0" "$@"
!#
println("Hello, world!")
args foreach println

它的原理是在代码第一次执行后,生成相应的jar文件,当下次再执行的便调用该jar文件来执行,第一次执行后生成的jar文件如下: 

3. 结束语

本节内容是scala入门到精通系列课程的最后一节,通过本课程,我相信可以让大家成为一个中级scala语言开发者。Scala语言功能非常强大,其中内容还有很多,还有许多内容我们没有涉及,例如scala 的GUI编程、Scala的定界延续等,但这些功能在实际开发中应用的并不是特别广泛,特别是Scala GUI编程,我们知道java在GUI编程方面并不是它的强项,scala语言也是如此。这门课程的目的是让大家学完之后能够快速上手spark应用程序开发,希望在学完本课程之后,大家将这门课作为自己学习scala的起点,而非终点。

原文链接

本文为云栖社区原创内容,未经允许不得转载。

© 著作权归作者所有

共有 人打赏支持
阿里云云栖社区
粉丝 63
博文 684
码字总数 1509769
作品 0
朝阳
私信 提问
浅谈 Scala 中下划线的用途

Scala 作为一门函数式编程语言,对习惯了指令式编程语言的同学来说,会不大习惯,这里除了思维方式之外,还有语法层面的,比如 underscore(下划线)就会出现在多种场合,令初学者相当疑惑,...

大数据之路
2015/04/23
0
3
免费的计算机编程类中文书籍

免费的编程中文书籍索引,欢迎投稿。 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程类书籍有哪些? 中文版 github 上的一个流行的编...

justjavac
2014/08/13
2.6K
10
编程类开放书籍荟萃(转载)

关于开源图书有人在网络上做了大量整理,本文为大家刊载《免费的编程中文书籍索引》 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程...

行者PHPer
2016/10/09
77
0
python入门编程

Python突击—从入门到精通到项目实战 python语言近年来越来越被程序相关人员喜欢和使用,因为其不仅简单容易学习和掌握,而且还有丰富的第三方程序库和相应完善的管理工具;从命令行脚本程序...

铁扇公主1
2017/03/05
102
0
分享两套Spark大数据视频教程

第一套: 课程目录: Scala基础与实践(上) Scala基础与实践(中) Scala基础与实践(下) Spark概述与编程模型(上) Spark概述与编程模型(下) 深入Spark内核(上) 深入Spark内核(下) Spark Streami...

查看地址
2014/11/17
51
3

没有更多内容

加载失败,请刷新页面

加载更多

Ubuntu常用操作

查看端口号 netstat -anp |grep 端口号 查看已使用端口情况 netstat -nultp(此处不用加端口号) netstat -anp |grep 82查看82端口的使用情况 查找被占用的端口: netstat -tln netstat -tl...

hc321
昨天
0
0
网站cdn的静态资源突然访问变的缓慢,问题排查流程

1.首先我查看了一下是否自己的网络问题,通过对比其他资源的访问速度和下载速度,确认不是 2.通过ping 和 tracert 判断cdn域名能否正常访问,(最后回想感觉这一步可以省略,因为每次最终能访...

小海bug
昨天
0
0
Mybatis 学习笔记四 MyBatis-Plus插件

Mybatis 学习笔记四 MyBatis-Plus插件 maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <ve......

晨猫
昨天
2
0
小白带你认识netty(二)之netty服务端启动(下)

承接上一篇小白带你认识netty(二)之netty服务端启动(上),还剩下两步骤:3、注册Selector:将Channel注册到Selector上 和 4、端口的绑定:服务端端口的监听。 3、注册Selector:将Chann...

天空小小
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部