shellスクリプトでサーバ間の差分を出すときにいろいろ覚えた

shellスクリプトをつかってみる

菊池です
つい最近、本番のサーバとステージングの環境でファイルやディレクトリの権限の差異が発生して
差分を調査しなければならないことがありました。

サーバが1つや2つなら,treeコマンドやlsコマンドで出力したものを印刷でもして見比べればいいのですが

今回は10台以上のサーバの差分を出さなければならなかったので

スクリプトを書こうと思いました
今までであれば簡単なスクリプトでもpythonやrubyを使って書いていたのですが

今回はshellスクリプトに挑戦してみました。

ls?tree?find?

まずはじめにファイルのアクセス権とオーナーと言った情報とファイルやディレクトリの一覧を取得することになります
ファイルの一覧といえばまっさきに思いつくのはlsやtreeコマンドでした
どちらもオプションをつけることで再帰的に末端のファイルまで必要な情報とともに出力してくれます
しかし、treeコマンドの場合

tree -f
  │       └── ./workspace/drinqApp/drinq/settings.gradle
    ├── ./workspace/ex
    │   ├── ./workspace/ex/Elixir.Fib.beam
    │   ├── ./workspace/ex/chanel_sample
    │   │   ├── ./workspace/ex/chanel_sample/README.md
    │   │   ├── ./workspace/ex/chanel_sample/_build
    │   │   │   └── ./workspace/ex/chanel_sample/_build/dev
    │   │   │       ├── ./workspace/ex/chanel_sample/_build/dev/consolidated
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Collectable.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.DBConnection.Query.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Ecto.DataType.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Ecto.Queryable.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Enumerable.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.IEx.Info.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Inspect.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.List.Chars.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Phoenix.HTML.FormData.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Phoenix.HTML.Safe.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Phoenix.Param.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Plug.Exception.beam
    │   │   │       │   ├── ./workspace/ex/chanel_sample/_build/dev/consolidated/Elixir.Poison.Decoder.beam

出力に出てくるラインが邪魔で加工しにくいです。軽くディレクトリの構造などを見渡すにはいいのですが今回はボツです! ><

次にlsですが

ls -Rl

...

./workspace/ex/hello/_build/dev/lib/gettext/ebin:
合計 260
-rw-rw-r-- 1 yakipote yakipote  1860 11月  6 02:17 Elixir.Gettext.Application.beam
-rw-rw-r-- 1 yakipote yakipote 10148 11月  6 02:17 Elixir.Gettext.Backend.beam
-rw-rw-r-- 1 yakipote yakipote 15724 11月  6 02:17 Elixir.Gettext.Compiler.beam
-rw-rw-r-- 1 yakipote yakipote  2256 11月  6 02:17 Elixir.Gettext.Error.beam
-rw-rw-r-- 1 yakipote yakipote 16452 11月  6 02:17 Elixir.Gettext.Extractor.beam
-rw-rw-r-- 1 yakipote yakipote  6056 11月  6 02:17 Elixir.Gettext.ExtractorAgent.beam
-rw-rw-r-- 1 yakipote yakipote  5520 11月  6 02:17 Elixir.Gettext.Fuzzy.beam
-rw-rw-r-- 1 yakipote yakipote  6328 11月  6 02:17 Elixir.Gettext.Interpolation.beam
-rw-rw-r-- 1 yakipote yakipote 12236 11月  6 02:17 Elixir.Gettext.Merger.beam
-rw-rw-r-- 1 yakipote yakipote  2976 11月  6 02:17 Elixir.Gettext.MissingBindingsError.beam
-rw-rw-r-- 1 yakipote yakipote  9728 ^C

なかなかいい感じなのですが、今回、同じファイル名で二箇所にあったり違う場所にあったりするので
ファイル名はフルパスでほしいので・・lsも今回はボツです

次にfind

find ./ -ls
  7209655      8 -rw-rw-r--   1 yakipote yakipote      4647 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/type_test.go
  7209638      4 drwxrwxr-x   2 yakipote yakipote      4096 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata
  7209649     12 -rw-rw-r--   1 yakipote yakipote      9509 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/split.elf
  7209650      4 -rw-rw-r--   1 yakipote yakipote      2083 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/typedef.c
  7209647     12 -rwxrwxr-x   1 yakipote yakipote     10348 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/ranges.elf
  7209639      4 -rw-rw-r--   1 yakipote yakipote       103 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/cycle.c
  7209645      4 -rw-rw-r--   1 yakipote yakipote        54 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/line2.c
  7209642     12 -rw-rw-r--   1 yakipote yakipote     10113 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/line-gcc.elf
  7209644      4 -rw-rw-r--   1 yakipote yakipote        83 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/line1.h。
  7209646      4 -rw-rw-r--   1 yakipote yakipote       415 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/ranges.c
  7209640      4 -rw-rw-r--   1 yakipote yakipote      2624 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/cycle.elf
  7209641     12 -rw-rw-r--   1 yakipote yakipote     10271 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/line-clang.elf
  7209648      4 -rw-rw-r--   1 yakipote yakipote        59 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/split.c
  7209643      4 -rw-rw-r--   1 yakipote yakipote        61 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/line1.c
  7209652     12 -rw-rw-r--   1 yakipote yakipote      9496 10月  9 11:40 ./.gvm/archive/go/src/debug/dwarf/testdata/typedef.elf

おお、まさに欲しかった結果だ
findコマンドは探し者をするときに使うものだと思っていたのですが今回のような場合にも有用みたいです。

ファイル名が一致するものを並べて表示する

同じ場所にある同じ名前のファイルとディレクトリを比べたいのですが・・
そこで、joinです。


sqlのjoinと同じような動作をするコマンドです
キーにするフィールドを指定して、一致するものを並べて表示します
今回はファイル名にあたるフィールドをキーにしてjoinしました
こうすることでアクセス権と所有者の違いを簡単に比べることができます。

さあ書くぞ

方針

  1. サーバの情報をfindでとる
  2. ソートする
  3. awkでほしいフィールドだけ出力する
  4. 比べたいサーバでも1,2,3をおこなう
  5. joinでいい感じにする

どんっ!

join -1 4 -2 4 \
&lt;(sshpass -p [password] ssh [username]@$1 find /data -ls \
 | awk '{print $3" "$5" "$6" "$11;}' |sort -k4) \
 &lt;(sshpass -p [password] ssh [username]@$2 find /data -ls \
| awk '{print $3" "$5" "$6" "$11;}' | sort -k4) | grep -v total \
| grep -v : |awk 'NF' &gt; hoge

糞読みにくいですけど、許してください
shellスクリプトの基礎がわかってないので、動くようになるまで大変でした

あとは・・並んで出力されたものを見比べて・・・
いやいやもちろん目視で確認なんかしませんよ!

違う箇所だけ出力します

cat hoge | while read line

do
    str1=`echo $line | awk '{print $2" "$3" "$4}'`
    str2=`echo $line | awk '{print $5" "$6" "$7}'`
    if [ "$str1" = "$str2" ]; then
        :     
    else
         echo -e ${line}
    fi
done

よかったよかった