Home About Contact
Gradle

split と cat を使って大きなファイルの転送途中の失敗に備える、およびその作業の Gradle SSH Plugin による自動化

諸事情により 相当大きなサイズのファイルを scp でサーバにアップロードすることがあるのだが、転送途中で接続が切れるなどの問題が生じるとはじめからやり直しになる。それを避けるために、split コマンドを使ってファイルを小分けにした上で転送して、サーバ上で cat で元に戻す、という昔からの方法がある。

しかしこの作業も繰り返し行うとなるとこれはほとんど拷問なので、Gradle SSH Plugin で半自動化して手間を省く話です。

split と cat によるサーバへのファイル転送

たとえば foo.tar.gz という大きなサイズのファイルがあったとする。

ロカールでの分割処理

split -b 10m foo.tar.gz foo.

こうすると foo.aa, foo.ab, foo.ac, .... という foo.tar.gz が分割されたファイルがカレントディレクトリに生成されるので、scp を使って以下のようにサーバへ転送します。

ls foo.?? | while read line; do scp $line hoge@servername:~/ ; done

これで foo.aa, foo.ab, foo.ac, .... ファイルは scp コマンドで サーバの hoge ユーザのホームディレクトリに転送されます。

※)もし途中で転送が失敗した場合は、転送済みファイルのみ削除して、再度実行する。

サーバので結合処理

全部の foo.?? ファイルをサーバに無事転送できたら、サーバ側で cat します。

cat foo.?? > foo.tar.gz

Gradle SSH Plugin で自動化

一度きりの作業ならこれでよいのですが、何度もこんな作業が発生すると気が滅入ります。対象が大きなファイルなので、全部転送するまでに時間がかかりますし、転送が終わったころを見計らって cat 作業を行わなければならないのはとても面倒です。

そこで Gradle SSH Plugin を使ってこの作業を自動化します。

build.gradle

plugins {
  id 'org.hidetake.ssh' version '1.4.0'
}

ssh.settings {
  dryRun = project.hasProperty('dryRun')
}

remotes {
  cloudsrv {
    role 'myworkspace'
    host = '192.168.0.99' // set your server IP address
    user = 'foo' // set your server unix username
    identity = file('/paht/to/id_rsa')
  }
}

task upload << {
  "split -b 10m foo.tar.gz foo.".execute().waitFor()

  def filter = { it.name=~/foo\.[a-z]{2}$/ }
  def filenamelist = new File('.').listFiles().findAll(filter).join(/ /)
  println filenamelist

  ssh.run {
    session(remotes.role('myworkspace')) {
      filenamelist.split(/ /).each { filename->
          println filename
          put( filename, filename )
      }
      execute( 'cat foo.?? > foo.tar.gz' )
    }
  }
}

これを foo.tar.gz ファイルが存在するディレクトリに build.gradle というファイル名で保存して

gradle upload

これで split/転送/cat を自動化できます。

まとめ

ネットワークが安定していれば、そもそも分割などしないで foo.tar.gz を scp すればこんな面倒なことは不要なわけで...