Sunday, July 29, 2012

FileSync

Ввиду некоторого разгильдяйства, а может, трудно предсказуемого рабочего графика, приходится мне в основном работать мобильно, на ноутбуке. Очевидно, что работаю я не один, и труды свои надо шарить с коллегами, чтобы ребята что-то доделывали, а где-то и их начинания надо брать и мне в доработку. Системы совместной работы банальны - файловые шары, но это и хорошо, поскольку с ним достаточно просто рабоатать. Вот и озадачился я как бы мне синхронизовать свои "ноутбучные" папки с обшими на шарах.

Я пытался использовать штаный механизм Microsoft "Make Available Off-line", но почему-то до добра это не довело, коллеги смеются до сих пор: файлы, которые были доступны мне оффлайново вошли в какое-то состояние, когда их невозможно удалить даже администратору сервера. Разбираться было лень, оставил это, собственно, администратору сервера, а для себя решил, что никогда больше не буду использовать этот механизм.

Задачу свою решил несложной программкой на C#.

Конфиг имеет простой формат:
#########################
# FileSync config file. #
#########################
# Format:
# SourceDirectory|DestinationDirectory
#
# Lines started with '#' - are ignored i.e. comments.
# Spaces before and after are ignored as well.
#
\\VBOXSVR\Documents\bin|C:\Documents and Settings\vtd\My Documents\bin

Через "|" указываются директории для синхронизации, по одной паре на кждой строке.
С # начинается комментарий.

Имеется ряд флажков, -h - покажет по ним кратенькую справку.

Скомпилированная версия с примером конфига и лога доступна здесь.
В целом, если я буду как-то и модифицировать этот скриптик, я буду подкладывать новые версии в Google Docs, тогда как исходник иже - менять не буду, лень. Также пока лениво использовать всякие системы социального кодинга, в том числе и контролирующие версии, - все-таки не так часто я занимаются программонаписательством. Исходник, как и в прежние случаи, привожу для того, может, кому какие фрагменты пригодятся для своих нужд.

Исходник - см. ниже:

   1:  using System;
   2:  using System.IO;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Text;
   6:  using System.Text.RegularExpressions;
   7:   
   8:   
   9:  namespace FileSync
  10:  {
  11:      class Program
  12:      {
  13:          private static bool SaveBack = false;
  14:          private static bool DeleteBack = false;
  15:          private static Regex BackFile;
  16:          private static bool OneWay = false;
  17:   
  18:          private static string Path1, Path2;
  19:          private static StreamWriter Log;
  20:          private static string ConfFileName = "FileSync.conf";
  21:          private static string LogFileName = "FileSync.log";
  22:   
  23:          private static string getLogDate()
  24:          {
  25:              return DateTime.Now.ToLocalTime().ToString() + "." + DateTime.Now.Millisecond.ToString();
  26:          }
  27:  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  28:          private static void ProcessDirectory(string path)
  29:          {
  30:              string[] fa = Directory.GetFiles(path);
  31:              foreach (string f in fa)
  32:              {
  33:                  if(DeleteBack && BackFile.IsMatch(f)){
  34:                      try { File.Delete(f); }
  35:                      catch (Exception e) { Console.WriteLine("ERROR File.Delete: " + e.ToString()); }
  36:                      
  37:                      Log.WriteLine(getLogDate() + " DelBack: " + f);
  38:                      continue;
  39:                  }
  40:                  if ((File.GetAttributes(f) & FileAttributes.Hidden) == FileAttributes.Hidden) continue;
  41:   
  42:                  string f2 = f.Replace(Path1, Path2);
  43:                  //Console.WriteLine("ProcessDirectory: file: '" + f + "' <> '"+f2+"'");
  44:                  
  45:                  DateTime dt1 = File.GetLastWriteTimeUtc(f);
  46:                  DateTime dt2 = File.GetLastWriteTimeUtc(f2);
  47:                  if ( DateTime.Compare(dt1,dt2)>0 )
  48:                  {
  49:                      //Console.WriteLine("Date compare " + dt1.ToString() + " > " + dt2.ToString());
  50:                      if (File.Exists(f2))
  51:                      {
  52:                          Log.WriteLine(getLogDate() + " CopywRewrite: " + f + " (" + dt1.ToLocalTime().ToString() + ") > "
  53:                          + f2 + " (" + dt2.ToLocalTime().ToString() + ")");
  54:                          if (SaveBack)
  55:                          {
  56:                              //Console.WriteLine(dt2.ToString() + " == " + dt2.Ticks);
  57:                              string f2b = f2 + ".~fs" + dt2.Ticks;
  58:                              try { File.Move(f2, f2b); }
  59:                              catch (Exception e) { Console.WriteLine("ERROR File.Move: " + e.ToString()); }
  60:                              File.SetAttributes(f2b, FileAttributes.Hidden);
  61:                              Log.WriteLine(getLogDate() + " Backup: " + f2 + " > " + f2b);
  62:                          }
  63:                      }
  64:                      else Log.WriteLine(getLogDate() + " Copy: " + f + " > " + f2);
  65:   
  66:                      try { File.Copy(f, f2, true); }
  67:                      catch (Exception e) { Console.WriteLine("ERROR File.Copy: " + e.ToString()); }
  68:                  }
  69:              }
  70:   
  71:              string[] da = Directory.GetDirectories(path);
  72:              foreach (string d in da)
  73:              {
  74:                  string d2 = d.Replace(Path1, Path2);
  75:                  if (!Directory.Exists(d2))
  76:                  {
  77:                      try { Directory.CreateDirectory(d2); }
  78:                      catch (Exception e) { Console.WriteLine("ERROR Directory.CreateDirectory: " + e.ToString()); }
  79:                      Log.WriteLine(getLogDate() + " Create: " + d2);
  80:                  }
  81:                  ProcessDirectory(d);
  82:              }
  83:          }
  84:  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  85:          static void Main(string[] args)
  86:          {
  87:              for (int i = 0; i < args.Length; i++ )
  88:              {
  89:                  if (args[i] == "-b") SaveBack = true; 
  90:                  if (args[i] == "-o") OneWay = true;
  91:                  if (args[i] == "-d")
  92:                  {
  93:                      DeleteBack = true;
  94:                      BackFile = new Regex(@"\.~fs\d+$");
  95:                  }
  96:                  if (args[i] == "-h")
  97:                  {
  98:                      Console.WriteLine("FileSync - sycronize directories content. Normal run without parameters.");
  99:                      Console.WriteLine("\t" + ConfFileName + " - config file.");
 100:                      Console.WriteLine("\t" + LogFileName + " - log file.");
 101:                      Console.WriteLine("\t-b - save backup files before rewrite with newer version.");
 102:                      Console.WriteLine("\t-d - delete previously saved backup files.");
 103:                      Console.WriteLine("\t-o - One way. Copy newer files from SourceDir to DestinationDir, and not from DestinationDir to SourceDir.");
 104:                      Console.WriteLine("\t-h - show this help.");
 105:                      Console.WriteLine("Freeware. (c) Sergey V Soldatov, 2012-07-29");
 106:                      Console.WriteLine("Any part of this software can be used as desired.");
 107:                      return;
 108:                  }
 109:              }
 110:              /*Console.WriteLine(""+SaveBack + OneWay + DeleteBack);//show options
 111:              return;*/
 112:   
 113:              if(File.Exists(ConfFileName)){
 114:                  FileInfo fil = new FileInfo(LogFileName);
 115:   
 116:                  try {
 117:                      Log = fil.AppendText();
 118:                      Log.WriteLine(getLogDate() + " Started: "+String.Join(" ",args));
 119:   
 120:                      StreamReader sr = File.OpenText(ConfFileName);
 121:                      string l = "";
 122:                      while( (l = sr.ReadLine()) != null){
 123:                          l = l.Trim();
 124:                          if (l.IndexOf('#') == 0) continue; //comment
 125:   
 126:                          string[] dirs = l.Split('|'); // '|' - delimeter
 127:                          //Console.WriteLine("dir0 = '" + dirs[0] + "'; dir1 = '"+dirs[1]);
 128:                          if (!Directory.Exists(dirs[0]))
 129:                          {
 130:                              Console.WriteLine("ERROR: Source Directory " + dirs[0] + " does not exists!");
 131:                              continue;
 132:                          }
 133:                          if (!Directory.Exists(dirs[1]))
 134:                          {
 135:                              Directory.CreateDirectory(dirs[1]);
 136:                              Log.WriteLine(getLogDate() + " Create: " + dirs[1]);
 137:                          }
 138:                          //Console.WriteLine("ProcessDirectory '" + dirs[0] + "'");
 139:                          Log.WriteLine(getLogDate() + " Sync: " + dirs[0] + " > " + dirs[1]);
 140:                          Path1 = dirs[0];
 141:                          Path2 = dirs[1];
 142:                          ProcessDirectory(dirs[0]);
 143:                          
 144:                          //Console.WriteLine("ProcessDirectory '" + dirs[1] + "'");
 145:                          if (!OneWay)
 146:                          {
 147:                              Log.WriteLine(getLogDate() + " Sync: " + dirs[1] + " > " + dirs[0]);
 148:                              Path1 = dirs[1];
 149:                              Path2 = dirs[0];
 150:                              ProcessDirectory(dirs[1]);
 151:                          }
 152:                          Log.WriteLine(getLogDate() + " Finished");
 153:                          Log.Flush();
 154:                          Log.Close();
 155:                      }
 156:                  }
 157:                  catch(Exception e){
 158:                      Console.WriteLine("ERROR: "+e.ToString());
 159:                  }
 160:              }
 161:              else {
 162:                  Console.WriteLine("ERROR: No Config found!");
 163:                  return;
 164:              }
 165:          }
 166:      }
 167:  }

3 comments:

Anonymous said...

Есть отличная прога - goodsync

Sergey Soldatov said...

Спасибо!
Меня только вот это смутило:
http://www.goodsync.com/ru/how-it-works/free-vs-pro

Здесь же - никаких ограничений :-), причем, если что-то не нравится, можно самому поправить как хочется.

whitenoise said...

Пользовался когда-то Microsoft Sync Toy, был доволен:
http://www.microsoft.com/en-us/download/details.aspx?id=15155