E.L.K. (7ocb) wrote in sokolsoft,
E.L.K.
7ocb
sokolsoft

  • Mood:
  • Music:

TFD release 1.2

Собственно, дописал и слегка потестил. Вот, выкладываю.
Тут два файла, собственно сам скрипт и .tfd - конфиг к нему.

Заранее прошу прощения за корявый стиль. ((:



Это код скрипта:
---------
# -*- coding: utf-8 -*-

##  TFD - temporary file deleter
##  Программа для удаления и/или архивации временных файлов.
##  Версия: 1.2
##  (c) Copyright E.L.K. 2005.
##
##  Тип лицензии: GNU General Public License
##
##  Эта программа является свободнораспространяемым программным обеспечением.
##  Вы можете перераспространять ее, использовать ее части или модифицировать
##  согласно  с  условиями  последней  версии  GNU  General  Public  License,
##  в том виде, в котором она опубликована Free Software Foundation.
##
##  Данная  программа  распространяется  в надежде, что она будет полезна, но
##  БЕЗ  ВСЯКИХ  ГАРАНТИЙ.  Автор,  либо  распространители  данной  программы
##  НЕ  НЕСУТ  НИКАКОЙ  ОТВЕТСТВЕННОСТИ  за  возможный  ущерб,  материальный,
##  моральный или другой, причиненный использованием данной программы.
##
##  Чтобы   получить  более  полное представление,  ознакомтесь  с оригиналом
##  GNU General Public License на сайте Free Software Foundation.
##  http://www.fsf.org
 
import time
import os
import sys
import string
import re
import time
import zipfile

config_paths = []


## Ф-ция удаляет ведущие и закрывающие пробельные символы в строке.
## Например: " some test\n " -> "some test"
def del_be_spaces(strng):
  strng = strng[:]
  a = 0
  for a in xrange(len(strng)):
    if strng[a] not in string.whitespace: break

  strng = strng[a:]
  for a in xrange(len(strng)-1, -1, -1):
    if strng[a] not in string.whitespace: break

  return strng[:a+1]


## Эта ф-ция проверяет, добавлен ли в конце пути разделитель, характерный для данной операционки.
## Если нет, добавляет его
def check_n_add_sep(inp):
  if inp[-len(os.path.sep):] == os.path.sep: return inp
  else:
    inp += os.path.sep
    return inp


 
## Из строки получаем подстроку, которая вероятнее всего в данном контексте является путем.
def get_path(inp):
  temp = inp.split("\"")
  if len(temp) == 3: return check_n_add_sep(temp[1])
  else: return check_n_add_sep(del_be_spaces(temp[0]))


## Ф-ция для загрузки конфигурации из файла с именем name. Заполняет список config_paths элементами вида
## (путь_где_смотреть, путь_куда_класть_логи, путь_куда_класть_архивы, флаги). Обязательным является только
## путь_где_смотреть. Если не указан путь_куда_класть_архивы, то архивирование не производится, даже если
## указан режим "а".
def load_cfg(name):
  cfg = open(name, "rt")
  temp = cfg.readline()
  while len(temp) != 0:   # будем считывать построчно весь файл.
    temp = del_be_spaces(temp)  # обрежем лишние пробелы в начале и в конце
    if temp[0] != "#" and len(temp) != 0 and temp[:8] == "SrcPath:":  # если это выполняется - то это начало блока.
      log_path = ""       # очистим переменные
      store_path = ""     #
      flags = ""
      src_path = get_path(temp[8:]) # найденный путь, который начало блока, сразу сохраним
      temp = cfg.readline()         # и считаем следующую строку,
      while len(temp) != 0:         # и, если она не последняя
        temp = del_be_spaces(temp)  # обрежем лишние пробелы в начале и в конце
        if temp[0] != "#" and len(temp) != 0:   # если это не комментарий и не пустая строка
          if temp[:8] == "SrcPath:": break      # и, к тому-же, не начало нового блока (если начало - тогда с этим блоком все)
          elif temp[:8] == "LogPath:": log_path = get_path(temp[8:])        # если это путь_куда_класть_логи, то сохраним его
          elif temp[:10] == "StorePath:": store_path = get_path(temp[10:])  # а это аналогично, только для архивов.
          elif temp[:6] == "Flags:": flags = temp[6:]      # тут находятся флаги, которые копируются до конца строки.
        temp = cfg.readline()
       
      config_paths.append((src_path, log_path, store_path, flags))  # добавим в список путей составленную комбинацию.
    else:
      temp = cfg.readline()
  cfg.close()




date_now = time.localtime()[:3]


## Ф-ция возвращает истину, если кортеж, переданный ей, содержит дату меньше, чем сегодня.
## то есть если дата, переданная ей, это уже "вчерашний день". (:
def is_yesterday(date, dn):
  if len(date) != 0:
    if date[0] > dn[0]: return 0
    elif date[0] < dn[0]: return 1
    else: return is_yesterday(date[1:], dn[1:])
  else: return 0



## проверяет существование абсолютного пути и в случае его несуществования рекурсивно пытается его создать.
## возвращает 1, если путь был или успешно создан.
## иначе возвращает 0
def check_n_create_path(pth):
  if os.path.isdir(pth): return 1   # проверим существование всего пути. Если он есть - все ок, возвращаем 1.
  else:                  
    if check_n_create_path(os.path.split(pth)[0]):  # если этого пути нет, тогда обрубаем последний узел и отдаем результат рекурсивно себе-же.
      try:
        os.mkdir(pth)     # в случае, если рекурсивный вызов дал 1, то есть весь предыдущий путь уже существует, пробуем создать последний узел.
      except:
        return 0          # какая бы ошибка не была - все, возвращаем 0
      if os.path.isdir(pth): return 1 # В случае, если путь был успешно создан, возвращаем 1.
    else:
      return 0
  return 0



ERR_NOOP     = -1
ERR_SUCCESS  =  0
ERR_ERROR    =  1
ERR_ABORTED  =  2

## Проверяет - переданный ей путь является ли папкой. Если являеся - рекурсивно передает ее самой себе.
## если не является - работаем.
def recurse_work(level, arc, path_from, path_to, ifdel, logfile):
  pack_error = ERR_SUCCESS
  del_error = ERR_SUCCESS
  if os.path.isdir(path_from):
    for dl in os.listdir(path_from):
      nfrom = os.path.join(path_from, dl)
      nto = os.path.join(path_to, dl)
      recurse_work(level+1, arc, nfrom, nto, ifdel, logfile)
    if ifdel: os.rmdir(path_from)
  else:
    try:
      if arc:
        arc.write(path_from, path_to)                 # если задан архив - пихаем файл в архив
      else:
        pack_error = ERR_NOOP
        ## Прошу обратить внимание!
        ## Вот тут обнаружился странный баг. То, что в кодировке cp1251, если я не ошибаюсь, в архивы запихивается с корявыми именами.
        ## очевидно, что где то ошибка с кодировками... вот только где? и как ее исправить?
      try:
        if ifdel: os.remove(path_from)                    # и, потом, если надо, удаляем.
        else: del_error = ERR_NOOP
      except: del_error = ERR_ERROR
    except:
      pack_error = ERR_ERROR
      del_error = ERR_ABORTED

    if logfile:
      logfile.write(" "*(level) + " " + path_from)
      if arc:
        if pack_error == ERR_SUCCESS: logfile.write("  ----packed")
        elif pack_error == ERR_ABORTED: logfile.write("  ----pack ABORTED")
        elif pack_error == ERR_ERROR: logfile.write("  ----pack ERROR")
      if ifdel:
        if del_error == ERR_SUCCESS: logfile.write("  ----deleted")
        elif del_error == ERR_ABORTED: logfile.write("  ----del ABORTED")
        elif del_error == ERR_ERROR: logfile.write("  ----del ERROR")
      logfile.write("\n")




## Проверим, было ли переопределено местоположение конфигурационного файла.
## Если было, то загрузим конфигурацию оттуда, откуда указано.
## Опять таки неизвестно, будет ли файл там, где ожидается или нет, но надо быть готовым к худшему.
try: 
  if len(sys.argv) != 1:
    load_cfg(sys.argv[1])
  else:
    load_cfg(".tfd")
except IOError:
  print "Error: Unable to find/open/read config file."
  raw_input()


## Добавить!!! Флаг удаления пути где_искать из конфига, если это пути не существует!! Может пригодится, если логи кладутся
##  во временную папку


## индексы соответствующих элементов.
## Для читаемости. Она и так хромает....
EN_FROM_NAME = 0      # откуда искать
EN_LOG_NAME = 1       # куда класть логи
EN_TO_NAME = 2        # куда класть архивы
EN_FLAGS = 3          # очевидно, это флаги


##---------------------------------------------------------------
##   Собственно, сама программа.



for entry in config_paths:
  if os.path.isdir(entry[EN_FROM_NAME]):  # если путь не указывает на директорию, то работать с ним смысла не имеет.
    pth_list = os.listdir(entry[EN_FROM_NAME])   # получим список всего, что обретается в директории
    for pth in pth_list:              # пробежимся по этому списку
      temp_date = re.search("[ad]{1,2}-([0-9]){4,4}-([0-9]){1,2}-([0-9]){1,2}", pth)  # ищет подстроку вида "a-2005-10-10"
      if temp_date:
        modes = temp_date.string[temp_date.start(): temp_date.end()].split("-")  # выделим из строки режим, год, месяц и день,
                                                                                 # разбив строку по "-"
        filedate = []
        for a in modes[1:]: filedate.append(int(a))
        if is_yesterday(filedate, date_now):    # если файл уже свое отжил, то есть его пора удалять, проверим желаемое для него действие
         
          if "a" in modes[0]:
            if len(entry[EN_TO_NAME]) != 0:   # но если не указана директория, в которую класть архивы, то действие выполнить не возможно,
                                              # поэтому ничего делаться не будет
              if check_n_create_path(entry[EN_TO_NAME]):  # проверим наличие директории, если ее нет, попробуем создать.
                if "arc_remove_date" in entry[EN_FLAGS]: # если указан этот флаг, обработаем имя файла, чтобы убрать дату
                  ## Вообще это достаточно интересная возможность - убрать дату "устаревания" из имени архива,
                  ## потому как если у нас есть куча директорий, названия которых отличаются только датой "устаревания",
                  ## то по ре удаления директорий их содержимое будет все время добавляться в один архив.
                  tbd = temp_date.string[:temp_date.start()]   # кусок имени до ключа обработки
                  tad = temp_date.string[temp_date.end():]   # кусок имени после ключа обработки
                  if tbd[-1:] == tad[:1]: tbd = tbd[:-1]
                  temp = os.path.join(entry[EN_TO_NAME], tbd+tad)   # получим имя архива без даты
                else: 
                  temp = os.path.join(entry[EN_TO_NAME], pth)       # получим имя архива без изменений
             
                try:
                  if os.path.isfile(temp+".zip"):  arc = zipfile.ZipFile(temp+".zip", "a", zipfile.ZIP_DEFLATED)
                  else: arc = zipfile.ZipFile(temp+".zip", "w", zipfile.ZIP_DEFLATED)
                except:
                  print "Error: Unable to open/create archive."
                  continue
              else:
                print "Error: Can't create/find archive directory."
                continue
            else:
              print "Error: No archive directory specified."
              continue
          else:
            arc = None

          if "d" in modes[0]: will_del = 1
          else: will_del = 0

          if len(entry[EN_LOG_NAME]) != 0:              # если указана директория, куда класть логи
            if check_n_create_path(entry[EN_LOG_NAME]): # и если она существует или успешно создана
           
              if "log_remove_date" in entry[EN_FLAGS]:   # из имени логов тоже можно убрать время устаревания.

                tbd = temp_date.string[:temp_date.start()]   # кусок имени до ключа обработки
                tad = temp_date.string[temp_date.end():]   # кусок имени после ключа обработки
                if tbd[-1:] == tad[:1]: tbd = tbd[:-1]
                temp = os.path.join(entry[EN_LOG_NAME], tbd+tad)   # получим имя лога без даты
              else: 
                temp = os.path.join(entry[EN_LOG_NAME], pth)       # получим имя лога без изменений
               
              logfile = open(os.path.join(temp+".log"),"at")
            else: logfile = None
          else: logfile = None

          recurse_work(0, arc, entry[EN_FROM_NAME]+pth, pth, will_del, logfile)

          if arc: arc.close()
          if logfile: logfile.close()
      
  else:
    print "Warning: invalid entry SrcPath: \"" + entry[EN_FROM_NAME]+ "\" - not a directory."


---------

Это файл .tfd

## Конфигурационный файл для проги, архивирующей и/или удаляющей "устаревшие" файлы.
## Файл конфигурации разбит на секции, каждая из которых начинается с блока ScrPath.
## SrcPath - это путь, который надо проверять на предмет "устаревших" файлов
## В блоке возможны следующие подблоки:
## StorePath: - путь, в который класть архивы.
## LogPath: - путь, в который класть логи.
## Flags: - флаги для этого блока.
## Пока что имеет смысл использовать два флага:
## Флаг: arc_remove_date удаляет из имени архива дату "устаревания".
## Флаг: log_remove_date удаляет из имени лога дату "устаревания".
## Пример:
#
# SrcPath: d:\temp\test   - тут будет производится осмотр.
# StorePath: "D:\temp\test\arc"   - сюда будут складываться архивы.
# LogPath: "d:\temp\test\log"      - сюда логи.
# Flags: arc_remove_date log_remove_date    - причем и у архивов и у логов в имени не будет фигурировать дата "устаревания".
#
##  Любой элемент, кроме SrcPath может отсутствовать.
##  В случае отсутствия LogPath - логи вестись не будут.
##  В случае отсутствия StorePath - не будет производится архивация.
##  ВАЖНО: в случае, если указана архивация и удаление, но путь на архивацию не указан, удаление производится также не будет.
##
##  Также любой элемент кроме SrcPath может дублироваться в одном блоке.
##  Скрипт будет использовать последнее встреченное в блоке значение.
##  Следующая встреченная строка с SrcPath считается концом предыдущего блока и началом следующего.
##  Конфигурационный файл может содержать несколько блоков, начинающихся с SrcPath
##
##  Чтобы скрипт "заметил" директорию, в ее названии должна присутствовать
##  строка вида "операция-год4знака-месяц2знака-день2знака",
##  отделенная или не отделенная от имени файла любым способом.
##  Примеры:
#   директория "temp.a-2005-10-12" будет заархивирована 13 октября 2005 года.
#   директория "temp.d-2006-01-01" будет удалена 2 января 2006 года.
#
#   директория "temp.ad-2006-04-01" либо "temp.da-2006-04-01"
#       будет вместе с содержимым зажата в архив и удалена на старом месте
#       при следующем включении после 1 апреля 2006 года.
##
##
## Примечание: строки, начинающиеся с символа решетки, считаются комментариями и игнорируются.

# SrcPath: d:\temp\test
# LogPath: d:\temp\test\log
# StorePath: d:\temp\test\arc
# Flags:

  • Post a new comment

    Error

    default userpic
  • 10 comments