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…