使わなくなったパソコンにSambaを入れて、ファイル共有用のサーバを構築してみたでLAN内のパソコン用のファイルサーバを構築してしばらく経過した。
そろそろ重要なファイルが保管される様になってきたので、サーバのバックアップをとらないと不安になってきたわけで、バックアップの検討を始めた。
サーバのPCにもうひとつハードディスクを搭載してバックアップも良いけど、やっぱり他のPCに保管しておきたくなるので、LAN内の他のPCにバックアップするためのコードを書いてみることにした。
実行ファイル形式にしておけば、OS起動時に実行することができるわけで、Go言語で書いてみることにした。
ちなみにこの記事を書いている段階では、まだバックアップの仕組みはできていないので過程のメモということで、とりあえず今回は同じPC間でのディレクトリ構造とファイルのコピーまでやってみる。
※今回やったこと
gorawディレクトリにあるディレクトリ構造とファイルをgobackディレクトリにコピーする
Go言語にはWalkという便利な関数がある。
このWalkはディレクトリを指定すると、再帰的にディレクトリとファイルを探してきてくれるため、今回のバックアップですごく大事になる。
ということで、まずはWalkを試す。
func Walk | filepath - The Go Programming Language
調べるディレクトリの構造は下記の通り
saito@saito-U37VC:~ tree goraw
goraw
├── a
│ ├── a-a
│ │ ├── a-a-a
│ │ │ └── hoge.txt
│ │ └── huga.txt
│ ├── a-b
│ │ └── a-b-a
│ └── a-c
├── b
│ ├── b-b
│ │ └── sample.png
│ └── sample.ods
└── c
└── aaa.txt
9 directories, 5 files
backup.goというファイルを作成し、下記のコードでWalkを試す。
Walkはディレクトリを調べた後にCloseをしなくても良いっぽい
package main
import (
"fmt"
"log"
"os"
"path/filepath"
)
func main() {
srcdir := "/home/saito/goraw/"
err := filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Println(err)
}
//ディレクトリかどうかを調べる
if info.IsDir() {
fmt.Println(path + "はディレクトリです")
} else {
fmt.Println(path + "はファイルです")
}
return nil
})
if err != nil {
log.Println(err)
}
}
コードを保存して実行してみると
saito@saito-U37VC:~ go run backup2.go /home/saito/goraw/はディレクトリです /home/saito/goraw/aはディレクトリです /home/saito/goraw/a/a-aはディレクトリです /home/saito/goraw/a/a-a/a-a-aはディレクトリです /home/saito/goraw/a/a-a/a-a-a/hoge.txtはファイルです /home/saito/goraw/a/a-a/huga.txtはファイルです /home/saito/goraw/a/a-bはディレクトリです /home/saito/goraw/a/a-b/a-b-aはディレクトリです /home/saito/goraw/a/a-cはディレクトリです /home/saito/goraw/bはディレクトリです /home/saito/goraw/b/b-bはディレクトリです /home/saito/goraw/b/b-b/hukuyou_wakime2.pngはファイルです /home/saito/goraw/b/sample.odsはファイルです /home/saito/goraw/cはディレクトリです /home/saito/goraw/c/aaa.txtはファイルです
とりあえず、再帰的にディレクトリとファイルを調べてくれたことは確認できた。
次はバックアップ先のディレクトリ内にディレクトリ構造をコピーするコードを追加してみる。
Statにパスを指定すると、ファイルやディレクトリの構造を調べてくれる関数があるので利用する。
func Stat | os - The Go Programming Language
ディレクトリがなければ、Mkdirでディレクトリを作成する。
func Mkdir | os - The Go Programming Language
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
srcdir := "/home/saito/goraw/"
backupDir := "/home/saito/goback/"
var dir string //gorawディレクトリ以下のパスを保持するための変数
err := filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Println(err)
}
//ディレクトリかどうかを調べる
if info.IsDir() {
fmt.Println(path + "はディレクトリです")
//ディレクトリの場合は、バックアップの方にディレクトリがあるか調べて、なければ作る
dir = strings.Replace(path, srcdir, "", 1)
if _, err = os.Stat(backupDir + dir); err != nil {
fmt.Println(backupDir + dir + "ディレクトリを作成します")
err = os.Mkdir(backupDir+dir, 0777)
if err != nil {
log.Println(err)
}
}
} else {
fmt.Println(path + "はファイルです")
}
return nil
})
if err != nil {
log.Println(err)
}
}
太字で示した箇所が追加したコードです。
バックアップ先のディレクトリにバックアップ元にあるディレクトリがあるか調べて、なければ作成するという処理を追加しています。
このコードを保存して実行してみると、
saito@saito-U37VC:~ go run backup3.go /home/saito/goraw/はディレクトリです /home/saito/goraw/aはディレクトリです /home/saito/goback/aディレクトリを作成します /home/saito/goraw/a/a-aはディレクトリです /home/saito/goback/a/a-aディレクトリを作成します /home/saito/goraw/a/a-a/a-a-aはディレクトリです /home/saito/goback/a/a-a/a-a-aディレクトリを作成します /home/saito/goraw/a/a-a/a-a-a/hoge.txtはファイルです /home/saito/goraw/a/a-a/huga.txtはファイルです /home/saito/goraw/a/a-bはディレクトリです /home/saito/goback/a/a-bディレクトリを作成します /home/saito/goraw/a/a-b/a-b-aはディレクトリです /home/saito/goback/a/a-b/a-b-aディレクトリを作成します /home/saito/goraw/a/a-cはディレクトリです /home/saito/goback/a/a-cディレクトリを作成します /home/saito/goraw/bはディレクトリです /home/saito/goback/bディレクトリを作成します /home/saito/goraw/b/b-bはディレクトリです /home/saito/goback/b/b-bディレクトリを作成します /home/saito/goraw/b/b-b/sample.pngはファイルです /home/saito/goraw/b/sample.odsはファイルです /home/saito/goraw/cはディレクトリです /home/saito/goback/cディレクトリを作成します /home/saito/goraw/c/aaa.txtはファイルです
上記のように実行され、
saito@saito-U37VC:~ tree goback goback ├── a │ ├── a-a │ │ └── a-a-a │ ├── a-b │ │ └── a-b-a │ └── a-c ├── b │ └── b-b └── c 9 directories, 0 files
実際にtreeコマンドで調べてみても、ディレクトリ構造のコピーができている。
最後にファイルをコピーしてみる。
ファイルのコピーはコピー元のファイルをOpenで開き、コピーしたい箇所にCreateでファイルを作成しておき、最後にCopyでファイルの内容をコピーする。
func Create | os - The Go Programming Language
func Copy | os - The Go Programming Language
で、追加したコードは下記の通り
※自身が理解しやすい様に重複している処理があります
package main
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
)
func main() {
srcdir := "/home/saito/goraw/"
backupDir := "/home/saito/goback/"
var dir string
var fpath string
var src *os.File
var dst *os.File
err := filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Println(err)
}
//ディレクトリかどうかを調べる
if info.IsDir() {
fmt.Println(path + "はディレクトリです")
//ディレクトリの場合は、バックアップの方にディレクトリがあるか調べて、なければ作る
dir = strings.Replace(path, srcdir, "", 1)
if _, err = os.Stat(backupDir + dir); err != nil {
fmt.Println(backupDir + dir + "ディレクトリを作成します")
err = os.Mkdir(backupDir+dir, 0777)
if err != nil {
log.Println(err)
}
}
} else {
fmt.Println(path + "はファイルです")
fpath = strings.Replace(path, srcdir, "", 1)
//ファイルをコピーする
fmt.Println(info.Name() + "をコピーします")
src, err = os.Open(path)
if err != nil {
log.Println(err)
}
dst, err = os.Create(backupDir + fpath)
if err != nil {
log.Println(err)
}
if _, err = io.Copy(dst, src); err != nil {
log.Println(err)
}
//今回はコピー用の関数を用意していないので、deferではなく直接Closeを指定
src.Close()
dst.Close()
}
return nil
})
if err != nil {
log.Println(err)
}
}
このコードを実行してみると
saito@saito-U37VC:~ go run backup.go /home/saito/goraw/はディレクトリです /home/saito/goraw/aはディレクトリです /home/saito/goraw/a/a-aはディレクトリです /home/saito/goraw/a/a-a/a-a-aはディレクトリです /home/saito/goraw/a/a-a/a-a-a/hoge.txtはファイルです hoge.txtをコピーします /home/saito/goraw/a/a-a/huga.txtはファイルです huga.txtをコピーします /home/saito/goraw/a/a-bはディレクトリです /home/saito/goraw/a/a-b/a-b-aはディレクトリです /home/saito/goraw/a/a-cはディレクトリです /home/saito/goraw/bはディレクトリです /home/saito/goraw/b/b-bはディレクトリです /home/saito/goraw/b/b-b/sample.pngはファイルです sample.pngをコピーします /home/saito/goraw/b/sample.odsはファイルです sample.odsをコピーします /home/saito/goraw/cはディレクトリです /home/saito/goraw/c/aaa.txtはファイルです aaa.txtをコピーします
実際のファイルシステムをtreeコマンドで確認してみると、
saito@saito-U37VC:~ tree goback
goback
├── a
│ ├── a-a
│ │ ├── a-a-a
│ │ │ └── hoge.txt
│ │ └── huga.txt
│ ├── a-b
│ │ └── a-b-a
│ └── a-c
├── b
│ ├── b-b
│ │ └── sample.png
│ └── sample.ods
└── c
└── aaa.txt
ファイルも無事コピーされたみたいだ。
テキストファイルやそれ以外のファイルを開いても、特にデータが損傷しているということもなく、コピー元と同じように開くことができた。
とりあえず、これで同じPC内であればバックアップができるようになったね。
ただし、
今回のコードでは、バックアップ先にファイルがあって、元のファイルに更新がなくても常にコピーされるという無駄な処理があるため、ファイルの更新時刻に合わせてコピーを行うかどうかという処理は入れておきたい。
-続く-




