Scala入門到精通——第三十節 Scala腳本編程與結束語

摘要: 本節主要內容 REPL命令行高級使用 使用Scala進行Linux腳本編程 結束語 1. REPL命令行高級使用 在使用REPL命令行時,有時候咱們須要粘貼的代碼比較大,而普通的粘貼可能會些一些問題,好比中文粘貼會出現亂碼、多行代碼粘貼時會出錯,此時須要用到REPL的高級功能。在平常開發過程當中,咱們粘貼多行代碼的時候會遇到下列問題: //本意是要粘貼下面兩行代碼java

本節主要內容

  1. REPL命令行高級使用
  2. 使用Scala進行Linux腳本編程
  3. 結束語

1. REPL命令行高級使用

在使用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

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的起點,而非終點。

原文連接

本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索