摘要: 本节主要内容 REPL命令行高级使用 使用Scala进行Linux脚本编程 结束语 1. REPL命令行高级使用 在使用REPL命令行时,有时候咱们须要粘贴的代码比较大,而普通的粘贴可能会些一些问题,好比中文粘贴会出现乱码、多行代码粘贴时会出错,此时须要用到REPL的高级功能。在平常开发过程当中,咱们粘贴多行代码的时候会遇到下列问题: //本意是要粘贴下面两行代码java
在使用REPL命令行时,有时候咱们须要粘贴的代码比较大,而普通的粘贴可能会些一些问题,好比中文粘贴会出现乱码、多行代码粘贴时会出错,此时须要用到REPL的高级功能。在平常开发过程当中,咱们粘贴多行代码的时候会遇到下列问题:mysql
//本意是要粘贴下面两行代码 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
而对于一些长串跨行的代码,可能会出现报错,例如:linux
//本意是要粘贴下面的代码 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命令行中执行下列命令:sql
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键,能够正常粘贴内容,中文也不会出现乱码了:shell
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
express
2 在文件中输入相应的scala语句,worksheet会自动打印出相应的结果
编程
可是worksheet对中文的支持很不友好,例以下面的代码:ubuntu
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最终获得的中文是乱码,所以在实际进行语言特性测试的时候尽可能避免中文sass
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
本节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文件以下:
本节内容是scala入门到精通系列课程的最后一节,经过本课程,我相信可让你们成为一个中级scala语言开发者。Scala语言功能很是强大,其中内容还有不少,还有许多内容咱们没有涉及,例如scala 的GUI编程、Scala的定界延续等,但这些功能在实际开发中应用的并非特别普遍,特别是Scala GUI编程,咱们知道java在GUI编程方面并非它的强项,scala语言也是如此。这门课程的目的是让你们学完以后可以快速上手spark应用程序开发,但愿在学完本课程以后,你们将这门课做为本身学习scala的起点,而非终点。
本文为云栖社区原创内容,未经容许不得转载。