Merhabalar,
Öncelikle karşılaştırma derken, nasıl bir karşılaştırma yapacağımızı anlatmalıyım. Performans işlemleri karşılaştırma kriterlerim içerisinde bulunmuyor, bundan hiç bahsetmeyeceğim. Ama kodun okunabilirliği, hızlı yazılması, hatalara karşı ne kadar duyarlı olduğu ve tabii ki en Önemlisi tekrar kullanabilirliği.
Aslında bu son nokta yani ‘tekrar kullanılabilirlik’ başlı başına bir yazı konusu ama buna şimdilik pek değinmeyeceğim. Başlıkta yazdığı gibi dosya işlemlerini karşılaştıracağım.
Arşılaştırmayı sadece VBScript, JScript ve Ruby arasında yapacağım, neden mi ? hali hazırda yapmıştım da ondan.. bu yazıyı yazacağım diye oturup program yazmadım. Önce programları yazdım, sonra yazıyı yazmak aklıma geldi. Bu nedenle neden diye sormayın. :)
Ama belki daha sonraki zamanlarda Java ve C# versiyonlarının karşılaştırmalarını da eklerim.
Öncelikle sizlere problemden bahsedeyim.
Belirli bir server üzerinde ps ve txt (post script ve text) dosyaları online bir uygulama tarafından oluşturuluyor, daha sonrasında online uygulama üzerinden kullanıcı bu dosyaları temizlemeyi unutuyor ve dosya sisteminin şişmesi ile beraber performans problemleri ortaya çıkıyor. Bu nedenle dosyaların gün bazında Ömürlerinin olmasına ve Ömrünü doldurmuş olan dosyalarında sistem tarafından silinmesine, silme işlemi esnasında log almasını ve bu loglarında aynı Ömür kuralına tabii olmasını istiyoruz… işte bu program bu işi yapacak.. günde bir kez çalışacak ve bu işlemi yapacak. Microsoft Windows sistemde çalışmasını istediğimiz için VBScript ve JScript’de (JavaScript’in bire bir aynı klonu) ve platform bağımsız olan Ruby’de yazıldı. İşte Ruby Örneği;
# Sabitler büyük harf ile tanımlanır. SOURCE_DRIVE_NAME = 'C' SOURCE_FOLDER_NAME = 'C:\FtpData' SOURCE_FILE_TYPES = 'txt,ps' LOG_FILE_NAME = 'Schedule_Job.txt' EXPIRY_DAY = 7 source_files = nil if SOURCE_FILE_TYPES.include?(",") source_files = File.join("**", "*.{#{SOURCE_FILE_TYPES}}") else source_files = File.join("**", "*.#{SOURCE_FILE_TYPES}") end # Belirtilen dizin mevcut mu ? if File.directory?(SOURCE_FOLDER_NAME) == false raise Exception.new("Folder does not exist (#{SOURCE_FOLDER_NAME})") end # Loglama işlemi için One Click-installer ile otomatik olarak gelen log4r kullanildi. require "log4r" toDay = Time.now log = Log4r::Logger.new('IS19660') logFile = Log4r::FileOutputter.new('IS19660', :filename => SOURCE_FOLDER_NAME + '\\' + toDay.strftime('%Y%m%d%H%M%S') + '_' + LOG_FILE_NAME, :trunc => true) log.outputters = logFile log.level = Log4r::INFO fileCTime = nil diff = 0.00 # Dizine geçildi Dir.chdir(SOURCE_FOLDER_NAME) # Dizin içerisinde ilgili dosyalar bulundu ve üzerinden gezildi. Dir.glob(source_files) do |fileName| fullFileName = SOURCE_FOLDER_NAME + "\\" + fileName.gsub('/','\\') # Bulunan dosyanın dizin olmadığı garanti altına alındı.. bazı durumda # Örnek dosya isminde 2 tane '.' olması durumunda bize dosya olarak dizinleri veriyor. next if FileTest.directory?(fullFileName) fileCTime = File.ctime(fullFileName) diff = (toDay - fileCTime) / 86400 if diff.floor >= EXPIRY_DAY begin File.delete(fullFileName) rescue log.info "Error at #{fullFileName}: #{$!.message}" else log.info "Deleted : #{fullFileName} at #{fileCTime.strftime('%d/%m/%Y %H:%M:%S')}" end else log.info "Skipped (Day) : #{fullFileName} at #{fileCTime.strftime('%d/%m/%Y %H:%M:%S')}" end end
GÖrüldüğü gibi toplamı 45 satır civarında ve işin yapıldığı kısım yaklaşık 15 satır. çok efektif ve anlaşılması çok kolay.
İşte sizlere VBScript hali.
' Tüm değişkenlerin tanımlı olması şarttır. Option Explicit Const SourceDriveName = "C" Const SourceFolderName = "C:\FtpData" Const SourceFileTypes = "txt,ps" Const LogFileName = "Schedule_Job.txt" Const ExpiryDay = 7 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Constants for opening files '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Const OpenFileForReading = 1 Const OpenFileForWriting = 2 Const OpenFileForAppending = 8 Sub WriteLog(Str) If Str <> "" Then LogStr = LogStr & Str & NewLine Else LogStr = LogStr & Err.Number & " - " & Err.Description & NewLine LogStr = LogStr & Err.Source & NewLine Err.Clear End If End Sub Sub ProcessFile(Folder) Dim Files, File, Diff, Index Dim FileExtension, TempArr, Skip Set Files = Folder.Files If Files.Count > 0 Then For Each File In Files TempArr = Split(File.Name, ".") FileExtension = TempArr(UBound(TempArr)) Skip = True For Index = LBound(FileTypes) To UBound(FileTypes) If FileTypes(Index) = FileExtension Then Skip = False Exit For End If Next If Skip = False Then Diff = DateDiff("d", File.DateCreated, ToDay) If Diff >= ExpiryDay Then WriteLog "Deleted: " & File.Path & " -- " & File.DateCreated & " -- " & ToDay & " -- " & Diff File.Delete If Err.Number <> 0 Then WriteLog "" End If Else WriteLog "Skipped (Day) : " & File.Path & " -- " & File.DateCreated & " -- " & ToDay & " -- " & Diff End If Else WriteLog "Skipped (Extension) : " & File.Path & " -- " & File.DateCreated & " -- " & ToDay End If Next End If End Sub Sub ProcessFolder(ParentFolder) Dim SubFolder, Folder Set SubFolder = ParentFolder.SubFolders If SubFolder.Count > 0 Then For Each Folder In SubFolder ProcessFolder Folder Next End If ProcessFile ParentFolder End Sub Sub Main() On Error Resume Next Dim SourceFolder, ToDayStr ToDayStr = Year(ToDay) & Right("00" & Month(ToDay), 2) & Right("00" & Day(ToDay), 2) & Right("00" & Hour(Time), 2) & Right("00" & Minute(Time), 2) & Right("00" & Second(Time), 2) Set FSO = CreateObject("Scripting.FileSystemObject") If FSO.DriveExists(SourceDriveName) AND FSO.FolderExists(SourceFolderName) Then Set SourceFolder = FSO.GetFolder(SourceFolderName) ProcessFolder SourceFolder If Err.Number <> 0 Then WriteLog "" End If If Trim(LogStr) <> "" Then Dim LogFile Set LogFile = FSO.CreateTextFile(SourceFolderName & "\" & ToDayStr & "_" & LogFileName, OpenFileForWriting, True) LogFile.Write LogStr LogFile.Close End If Else Dim LogFileInC Set LogFileInC = FSO.CreateTextFile("C:\" & ToDayStr & "_" & LogFileName, OpenFileForWriting, True) LogFileInC.Write LogStr LogFileInC.Write Err.Number & "-" & Err.Description & NewLine LogFileInC.Write Err.Source & NewLine LogFileInC.Close End If End Sub Dim NewLine, TabChar, LogStr, ToDay Dim FSO, FileTypes NewLine = Chr(10) TabChar = Chr(9) LogStr = "" ToDay = Date FileTypes = Split(SourceFileTypes, ",") Call Main()
Yaklaşık 115 satır, programın kendisi yaklaşık 80 satır kadar, içerisinde recursive bir method mevcut, bu nedenle takip etmesi ve anlaması oldukça zor, fakat kütüphane desteği yüksek. Bununla beraber çok ofansif bir program, herhangi bir hata yakalama yok, ‘On Error Resume Next’ ile hayatın çok güzel olduğu varsayılmış.
Şimdi JScript (JavaScript versiyonu)
var sourceDriveName = 'C'; var sourceFolderName = 'C:\\FtpData'; var sourceFileTypes = 'txt,ps'; var logFileName = 'Schedule_Job.txt'; var expiryDay = 7; /* constants for opening files */ var OpenFileForReading = 1; var OpenFileForWriting = 2; var OpenFileForAppending = 8; var logStr = ''; var fileTypes = new Array(); var toDay = new Date(); function print(str) { logStr += (str + '\n'); } // En azından sınıflar çalışma zamanında değiştirilebiliyor. Number.prototype.fixDigit = function (fixDigit$) { if (arguments.length != 1) { throw new Error('Wrong number of arguments'); } var length = arguments[0]; var retValue = this.toString(); var currentLength = retValue.length; if (currentLength >= length) { return retValue; } for (var index = 0; index < length - currentLength; index++) { retValue = '0' + retValue; } return retValue; } // format işlemini kendi kullanımıma gÖre yazdım. Date.prototype.format = function(format$) { return this.getFullYear().toString() + this.getMonth().fixDigit(2) + this.getDate().fixDigit(2) + ' ' + this.getHours().fixDigit(2) + ':' + this.getMinutes().fixDigit(2) + ':' + this.getSeconds().fixDigit(2); } // Off offf VBScript'in DateDiff'i var, JScript'in yeni oldu.. internetten buldum ama sonra bir şekilde bulduğum yeri kaybettim :(. // Bu nedenle nereden aldığımı yazamıyorum. Elbette bulduğum yerde bu şekilde bir prototype değildi. Bu hale ben getirdim. Date.prototype.dateDiff = function (h$) { if (arguments.length != 2) { throw new Error('Wrong number of arguments'); } var interval = arguments[0]; var dt1 = arguments[1]; var dt2 = this; var diffMS = dt2.valueOf() - dt1.valueOf(); var diff = new Date(diffMS); // calc various diffs var years = dt2.getUTCFullYear() - dt1.getUTCFullYear(); var months = dt2.getUTCMonth() - dt1.getUTCMonth() + (years==0 ? 0 : years*12); var quarters = parseInt(months/3); var milliseconds = diffMS; var seconds = parseInt(diffMS/1000); var minutes = parseInt(seconds/60); var hours = parseInt(minutes/60); var days = parseInt(hours/24); var weeks = parseInt(days/7); switch(interval.toLowerCase()){ case "yyyy": return years; case "q": return quarters; case "m": return months; case "d": return days; case "w": return days; case "ww": return weeks; case "h": return hours; case "n": return minutes; case "s": return seconds; case "ms":return milliseconds; default: return "invalid interval: '" + interval + "'"; } } function processFile(folder) { var files = new Enumerator(folder.Files); for (; !files.atEnd(); files.moveNext()) { var file = files.item(); var fileName = file.Name; var fileCreateDt = new Date(file.DateCreated); var fileExtension = fileName.split('.').pop(); var skipped = true; for (extension in fileTypes) { if (fileTypes[extension] == fileExtension) { skipped = false; break; } } if (!skipped) { if (toDay.dateDiff("d", fileCreateDt) >= expiryDay) { try { file.Delete(); print('Deleted :' + fileName + ' - ' + fileCreateDt.format()); } catch (e) { print('Error At ' + fileName + ':' + e.name + ' - ' + e.message); } } else { print('Skipped (Day) :' + fileName + ' - ' + fileCreateDt.format()); } } else { print('Skipped (Extension) :' + fileName + ' - ' + fileCreateDt.format()); } } } function processFolder(folder) { processFile(folder); var subFolders = new Enumerator(folder.SubFolders); for (; !subFolders.atEnd(); subFolders.moveNext()) { processFolder(subFolders.item()); } } function main() { var fso = new ActiveXObject('Scripting.FileSystemObject'); fileTypes = sourceFileTypes.split(','); try { if (fso.DriveExists(sourceDriveName) == false || fso.FolderExists(sourceFolderName) == false) { throw new Error('Folder or Drive does not exist.!!'); } processFolder(fso.GetFolder(sourceFolderName)); } catch (e) { if (typeof(e) == 'string') { print(e); } else { print(e.name + ' - ' + e.message); } } var toDayStr = toDay.getFullYear().toString() + (toDay.getMonth() + 1).fixDigit(2) + toDay.getDate().fixDigit(2); toDayStr += toDay.getHours().fixDigit(2) + toDay.getMinutes().fixDigit(2) + toDay.getSeconds().fixDigit(2); var logFile = null; try { logFile = fso.CreateTextFile(sourceFolderName + '\\' + toDayStr + '_' + logFileName, OpenFileForWriting, true); logFile.Write(logStr); } catch (e) { if (logFile != null) { logFile.Close(); } if (typeof(e) == 'string') { print(e); } else { print(e.name + ' - ' + e.message); } logFile = fso.CreateTextFile('C:\\' + toDayStr + '_' + logFileName, OpenFileForWriting, true); logFile.Write(logStr); } finally { if (logFile != null) { logFile.Close(); } } return 0; } main();
Yaklaşık 160 satır, programın kendisi 130 satır civarında, recursive bir fonksiyona sahip, kütüphane desteği az olduğu için (neyseki prototype tabanlı) Date sınıfına dateDiff ve format methodları ile Number sınıfına fixDigit methodu eklenmiştir. Tamaniyle defansif bir yazım tekniği uygulanmıştır. prototype bazlı olduğu için dateDiff, format ve fixDigit methodları tekrar rahatlıkla kullanılabilir, hatta sizlerde kullabilirsiniz.
Sonuç olarak; Ruby açık ara Önde, 2. sırada bence JScript geliyor(Hata yakalama işi Önemli bir kavram), en son VBScript.
Belki gelecek yazılarda Java, C# ve Python versiyonlarını yazabilirim.. belli olmaz :)
Kolay gelsin…