会社では定期的にScalaの新たなトピックを確認したり共有したりする会をやっている。そこでScalaのRedditを見ていると、os-libのバージョンアップが知らされていた。
見ると最近のバージョンではos-libはZIPを扱えるようになっていた。APIも便利そうなので紹介する。
os-libとは
そもそもos-libとは何かというと、lihaoyiさんという方が作っているScalaのライブラリで、ファイル操作やプロセス操作といったOS寄りのちょっとした操作を集めたユーティリティだ。
例えばコマンドを指定してプロセスを起動し、その標準出力をストリーミング形式で得るとか、ファイルを開いたり削除したり、一時ファイルを用意したりといった、シェルスクリプト的なおなじみの操作が用意されていて、しかもそのAPIがとても使いやすい。
val wd = os.pwd os.write(wd/"file.txt", "hello") // カレントディレクトリの"file.txt"に、"hello"を書き込む
os-lib 0.11.0
os-lib 0.11.0で、ZIPファイルへのサポートが追加された。
利用できるようになった主要なAPIを紹介する:
os.zip: ZIPファイルを作成する / 既存のZIPファイルに追加する
既存のファイルをZIPファイルに入れられる。
//> using scala 3.5.0//> using deps "com.lihaoyi::os-lib:0.11.1"val wd = os.pwd val file1 = wd / "file1.txt"val file2 = wd / "file2.txt"val file3 = wd / "file3.txt" os.write.over(file1, "Hello World!") os.write.over(file2, "Good World!") os.write.over(file3, "Nice World!") os.zip(wd / "zipfile.zip", Seq(file1, file2, file3), preserveMtimes = true)
os.zip.stream: ZIPファイルを作成するがファイルではなくストリームに出力する
os.write.outputStream
などと組み合わせられる。
//> using scala 3.5.0//> using deps "com.lihaoyi::os-lib:0.11.1"val wd = os.pwd val file1 = wd / "file1.txt"val file2 = wd / "file2.txt"val file3 = wd / "file3.txt" os.write.over(file1, "Hello World!") os.write.over(file2, "Good World!") os.write.over(file3, "Nice World!") val stream = os.zip.stream(Seq(file1, file2, file3), preserveMtimes = true) val zipFileStream = os.write.outputStream(wd / "files.zip") stream.writeBytesTo(zipFileStream) zipFileStream.close()
os.unzip: ZIPファイルをディスク上に解凍する
excludePatterns
、includePatterns
を使って出力を制御できるのが便利。この例ではfile2.txt
を除いてfiles
ディレクトリに解凍している(ディレクトリは自動的に生えてくるようだ)。
//> using scala 3.5.0//> using deps "com.lihaoyi::os-lib:0.11.1"val wd = os.pwd val zip = wd / "files.zip" os.unzip(zip, wd / "files", excludePatterns = Seq("file2.txt".r.anchored))
os.unzip.stream: ZIPファイルをjava.io.InputStream
からディスク上に出力する
これはos.zip.stream
と勝手は同じなので省略。
os.unzip.list: ZIPファイルの内容をリストアップする
地味に便利な機能としてリストアップ機能がついている。
//> using scala 3.5.0//> using deps "com.lihaoyi::os-lib:0.11.1"val wd = os.pwd val zip = wd / "files.zip" println(os.unzip.list(zip).toSeq) // => Vector(file1.txt, file2.txt, file3.txt)
os.zip.open: ZIPファイルをjava.nio.file.FileSystem
として開き、os.Path
を返して利用できるようにする
これも結構便利そう。あたかもファイルシステムの上にあるように見えるので、直接ファイルの中身を見たり書いたりできるようになる。
//> using scala 3.5.0//> using deps "com.lihaoyi::os-lib:0.11.1"val wd = os.pwd val zip = wd / "files.zip"val zipfilesystem = os.zip.open(zip) try println(os.read(zipfilesystem / "file1.txt")) // Hello, World!finally zipfilesystem.close()
番外: os.unzip.streamRaw これは内部API
内部ストリームを露出するAPIも用意されているが、これはどちらかといえばパワーユーザ向けだろう。
まとめ
os-libでZIPファイルを扱えるようになるとできることの幅がけっこう広がる。手でzip
を呼び出す必要がないのだ。この調子でgzipとかtarを扱えると嬉しいけれど、さすがにそこまでやるくらいならコマンドを呼び出したほうがよさそうだ。