1
2
use Text::Diff;
my $diff = diff ("test.txt", "test2.txt", { OUTPUT => sub { my $item = shift; if ($item =~ m{^(\+|-)/}) { push @output, $item } } } );
1
2
use Text::Diff;
my $diff = diff ("test.txt", "test2.txt", { OUTPUT => sub { my $item = shift; if ($item =~ m/^(?:\+|\-)\//) { push @output, $item } } } );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/perl
use strict;
use Text::Diff;
my @output;
my @db;
my @fs;
my $diff = diff ("test.txt", "test2.txt", { OUTPUT => \@output } );
print $output[0];
#while (@output) {
# my $item = shift(@output);
# if ($item =~ m/^\+\//) {
# $item =~ s/^\+\///;
# print $item;
# push (@db, $item);
# } elsif ($item =~ m/^-\//) {
# $item =~ s/^-\///;
# push (@fs, $item);
# }
#}
foreach my $dbitem (@db) {
print $dbitem;
}
foreach my $fsitem (@fs) {
print $fsitem;
}
@@ -15,7 +15,16 @@
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/ho
/ho
/ho
-/ho
+/ho
+/ho
+/ho
+/ho
+/ho
+/ho
+/ho
+/ho
+/ho
+/ho
/ho
/ho
/ho
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#!/usr/bin/perl use strict; use warnings; use Text::Diff; use Data::Dumper; my @output; my $diff = diff ("test.txt","test2.txt", { OUTPUT => sub { my $item = shift; for(split(/\n/,$item)) { push(@output, $_) if(m!^[+-]/!); } }}); print Dumper($diff,\@output);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/perl
use strict;
use Text::Diff;
my @db;
my @fs;
my $diff = diff ("test.txt","test2.txt", { OUTPUT => sub {
my $item = shift;
for(split(/\n/,$item))
{
if (m!^\+/!) {
push(@db, $_);
} elsif (m!^-/!) {
push(@fs, $_);
}
}
}});
foreach my $dbitem (@db) {
print $dbitem."\n";
}
foreach my $fsitem (@fs) {
print $fsitem."\n";
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
#!/usr/bin/perl use strict; use warnings; use Algorithm::Diff; use Tie::File; tie my @x, "Tie::File", $ARGV[0] or die $!; tie my @y, "Tie::File", $ARGV[1] or die $!; my $diff = Algorithm::Diff->new(\@x,\@y); my (@db, @fs); while ($diff->Next()) { next if $diff->Same(); push @db, $diff->Items(2); # hinzugekommen push @fs, $diff->Items(1); # weggefallen } foreach my $dbitem (@db) { print $dbitem, "\n"; } foreach my $fsitem (@fs) { print $fsitem, "\n"; }
1 2 3 4 5
while ($diff->Next()) { next if $diff->Same(); push @db, grep{ substr($_,0,1) eq '/' }$diff->Items(2); # hinzugekommen push @fs, grep{ substr($_,0,1) eq '/' }$diff->Items(1); # weggefallen }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
#!/usr/bin/perl use strict; use warnings; use Tie::File; tie my @x, "Tie::File", $ARGV[0] or die $!; tie my @y, "Tie::File", $ARGV[1] or die $!; my %diff; for(@x) { $diff{$_}{x}++ if(substr($_,0,1) eq '/'); } for(@y) { $diff{$_}{y}++ if(substr($_,0,1) eq '/'); } my (@db, @fs); for my $path (sort keys(%diff)) { my $elm=$diff{$path}; next if($elm->{x} && $elm->{y}) push(@db,$path) if($elm->{x}); push(@fs,$path) if($elm->{y}); } foreach my $dbitem (@db) { print $dbitem, "\n"; } foreach my $fsitem (@fs) { print $fsitem, "\n"; }
2011-08-08T01:30:38 topegEr arbeitet mit Verzeichnispfaden soweit ich es sehe.
QuoteUnd wo wir gerade von alternativen reden, Wenn es wirklich Verzeichnisse sind, ist es besser einen Hash zum vergleichen zu benutzen, denn ich wäre mir nicht sicher, ob die Pfade immer an der selben Stelle stehen. Selbst wenn man die Liste vorher sortiert, kann es sein, dass es falsche Funde gibt.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
#!/usr/bin/perl use strict; use warnings; use Set::Scalar; use Tie::File; tie my @x, "Tie::File", $ARGV[0] or die $!; tie my @y, "Tie::File", $ARGV[1] or die $!; my $vorher = Set::Scalar->new(@x); my $nachher = Set::Scalar->new(@y); my @db = $nachher->difference($vorher)->members; # hinzugekommen my @fs = $vorher->difference($nachher)->members; # weggefallen foreach my $dbitem (@db) { print "> ", $dbitem, "\n"; } foreach my $fsitem (@fs) { print "< ", $fsitem, "\n"; }
1
2
3
$a->difference($b, $c) (a b)
$a->symmetric_difference($b, $c) (a b e h i)
$a->unique($b, $c) (a b h i)
1
2
my @db = $nachher->unique($vorher)->members;
my @fs = $vorher->unique($nachher)->members;
2011-08-08T18:04:09 lousek1. ist mir nicht hunderprozentig klar, was Set::Scalar ist, die Doku sagt dazu nur "basic set operations";
Quote2. was ist der unterschied zwischen difference und unique resp warum verwendest du nicht unique?
Code: (dl )1
2
3$a->difference($b, $c) (a b)
$a->symmetric_difference($b, $c) (a b e h i)
$a->unique($b, $c) (a b h i)
QuoteDas unique hier scheint nicht wie das uniq vom linux commando zu sein, wo Zeilen, welche in beiden vorkommen, entfernt werden.
Quote[...] "tie" erübrigt sich also für mich, wenn ich das richtig sehe, aber ich werde es sicher in Zukunft mal brauchen können :)
QuoteDas Beispiel von Dubu sieht sehr simpel aber effizient aus (oder einfach besonders einfach für einen Perl-DAU wie mich :))
Aber könnte ich nicht einfach gerade schreiben:
Code: (dl )1
2my @db = $nachher->unique($vorher)->members;
my @fs = $vorher->unique($nachher)->members;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/perl
# Define options
use strict;
use warnings;
# Define used modules
use Text::Diff;
use DBI;
use File::Find;
use Set::Scalar;
# Define variables
my $db;
my $user;
my $pass;
my $host;
my $query;
my $sth;
my $dbh;
my $stime;
my $etime;
my $endtime;
my $stime_tmp;
my $etime_tmp;
my $endtime_tmp;
my @db;
my @fs;
my @only_db;
my @only_fs;
# DB data
$db = "ofrs";
$user = "ofrs";
$pass = "ofrs";
$host = "localhost";
# Connect to DB
$dbh = DBI->connect("DBI:mysql:$db:$host", $user, $pass);
# Set start time
$stime = time;
# Set query
$query = "SELECT id,path FROM repdir;";
# Run query
my $sth1 = $dbh->prepare($query);
$sth1->execute();
# Read query results into array @db
while (my @result = $sth1->fetchrow_array) {
checkRepDir($result[0], $result[1]);
}
# Set end time
$etime = time;
# Calculate used time
$endtime = ($etime - $stime);
# Print needed time
print "Total duration: $endtime sec\n";
########## Functions ##########
# compares a file tree from the file system with the path entries from the database. Adds paths, which are only on the filesystem to the database, updates changed paths in the database, remove missing paths from the database.
sub checkRepDir {
# Define variables
my @insert_db;
my @update_db;
my @delete_db;
# Set variables
my $repdir_id = $_[0];
my $repdir_path = $_[1];
print "Running check for replication directory $repdir_path with ID $repdir_id ...\n";
### Getting database paths ###
# Set query
$query = "SELECT path,ino,mtime,ctime FROM dir WHERE repdir_id = $repdir_id;";
print "Running DB query ... ";
$stime_tmp = time;
# Run query
$sth = $dbh->prepare($query);
$sth->execute();
# Read query results into array @db
while (my @result = $sth->fetchrow_array) {
push(@db,$result[0].";".$result[1].";".$result[2].";".$result[3]."\n");
}
$etime_tmp = time;
$endtime_tmp = ($etime_tmp - $stime_tmp);
print scalar(@db)." paths found! Duration: $endtime_tmp sec\n";
### Getting filesystem paths ###
print "Searching filesystem ... ";
$stime_tmp = time;
# Search file system
File::Find::find({wanted => sub{wanted();}, "no_chdir" => 0}, $repdir_path);
# read paths from filesystem into array @fs
sub wanted{
if (-d $File::Find::name) {
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat($File::Find::name);
push(@fs,$File::Find::name.";".$ino.";".$mtime.";".$ctime."\n");
}
}
$etime_tmp = time;
$endtime_tmp = ($etime_tmp - $stime_tmp);
print scalar(@fs)." paths found! Duration: $endtime_tmp sec\n";
### Find the added, updated and deleted paths ###
print "Comparing results ... ";
$stime_tmp = time;
my $temp1 = Set::Scalar->new(@db);
my $temp2 = Set::Scalar->new(@fs);
@only_db = $temp1->difference($temp2)->members;
@only_fs = $temp2->difference($temp1)->members;
$etime_tmp = time;
$endtime_tmp = ($etime_tmp - $stime_tmp);
print scalar(@only_fs)." paths only on FS! ".scalar(@only_db)." paths only in DB! Duration: $endtime_tmp sec\n";
### Updating the database with the newest information ### !!!A big mess at the moment, I will try to clean that up in the next step ...!!!
print "Updating database ... \n";
$stime_tmp = time;
$query = "";
foreach my $item_fs (@only_fs) {
# Split up the parts of the line from the filesystem into path, ino, mtime, ctime
my @parts_fs = split(/;/, $item_fs);
# If there are any items in @only_db, check if some lines have just a changed mtime or ctime, but not a new path. If @only_db contains no elements, then just add the path to the database.
if (@only_db > 0) {
foreach my $item_db (@only_db) {
# Split up the parts of the line from the database into path, ino, mtime, ctime
my @parts_db = split(/;/, $item_db);
# Check if the path and the inode from the filesystem-item and the database-item is the same. If yes, only the mtime or ctime has changed, so only update the database. If no, the path is really new, so just insert it into the database
if (($parts_fs[0] eq $parts_db[0]) && ($parts_fs[1] eq $parts_db[1])) {
# Path and inode is the same, only mtime or ctime has changed
$query = "UPDATE `ofrs`.`dir` SET `mtime` = '$parts_fs[2]' , `ctime` = '$parts_fs[3]' WHERE `repdir_id` = '$repdir_id' AND `path` = '$parts_fs[0]' AND `ino` = '$parts_fs[1]'; ";
last;
} else {
# Path and/or inode is not the same, so this is a complete new entry
$query = "INSERT INTO `ofrs`.`dir` (`id` , `repdir_id` , `path` , `ino` , `mtime` , `ctime`) VALUES (NULL , '$repdir_id' , '$parts_fs[0]' , '$parts_fs[1]' , '$parts_fs[2]' , '$parts_fs[3]'); ";
}
}
} else {
$query = "INSERT INTO `ofrs`.`dir` (`id` , `repdir_id` , `path` , `ino` , `mtime` , `ctime`) VALUES (NULL , '$repdir_id' , '$parts_fs[0]' , '$parts_fs[1]' , '$parts_fs[2]' , '$parts_fs[3]'); ";
}
# Run query
$sth = $dbh->prepare($query);
$sth->execute();
}
while (@only_db) {
# Split up the parts of the line from the database into path, ino, mtime, ctime
my @parts_db = split(/;/, shift(@only_db));
# If there are any items in @only_fs, check if the database-item was just updated (in the previous foreach-section), or if the database-item must be completly removed
if (@only_fs > 0) {
foreach my $item_fs (@only_fs) {
my @parts_fs = split(/;/, $item_fs);
if (($parts_db[0] eq $parts_fs[0]) && ($parts_db[1] eq $parts_db[1])) {
# Do nothing, already updated in the previous foreach-section
$query = "";
last;
} else {
# Path does not exist anymore on the filesystem, so delete it from the database
$query = "DELETE FROM `ofrs`.`dir` WHERE `path` = '$parts_db[0]' AND `ino` = '$parts_db[1]';";
}
}
} else {
# Path does not exist anymore on the filesystem, so delete it
$query = "DELETE FROM `ofrs`.`dir` WHERE `path` = '$parts_db[0]' AND `ino` = $parts_db[1]';";
}
if ($query ne "") {
# Run query
$sth = $dbh->prepare($query);
$sth->execute();
}
}
$etime_tmp = time;
$endtime_tmp = ($etime_tmp - $stime_tmp);
print scalar(@only_fs)." paths inserted! Duration: $endtime_tmp sec\n";
# Empty arrays
undef @fs;
undef @db;
undef @only_fs;
undef @only_db;
}
2011-08-10T08:43:50 lousekBezüglich den unnötigen SQL-Abfragen:
Eigentlich mache ich ja ein "genereller" SELECT (für die rep_dirs), dann pro rep_dir ein SELECT und dann für jedes neues Verzeichnis ein INSERT, für ein aktualisiertes ein UPDATE und für ein gelöschtes ein DELETE.
2011-08-10T08:43:50 lousekSehe ich das richtig, dass du sämtliche "direkte" Datenbankenfunktionen (sprich queries) als Referenzen(?) im Hash $sth_lst gespeichert hast?
Warum genau hast du z.B. den prepare im delete_old und nicht einfach im sub delete_old_files "gespeichert"?
2011-08-10T08:43:50 lousek- Die Funktion rel2abs konvertiert relative in absolute Pfade, aber $File::Find::Name würde ja schon den absoluten Pfad enthalten ... ?
2011-08-10T08:43:50 lousek- Es wird mit return 0 unless (-f $path) erneut überprüft, ob die Datei existiert. Aber das wurde ja bereits vor dem Aufruf von process_file erledigt (return unless -f) ... ?
2011-08-10T08:43:50 lousekAnschliessend wird überprüft ob der Pfad bereits in der DB steht. Aber wird hier nicht für jeden einzelnen Pfad einen eigenen SELECT gemacht?
Dies wäre doch eben wieder nicht gerade so effizient ... ?
Wenn der Pfad in der DB steht (defined($id)), so wird er aktualisiert wenn die atime, mtime, ctime, oder inode nicht übereinstimmt, ansonsten wird nur der status aktualisiert.
Wenn der Pfad nicht in der DB steht, wird er "logischerweise" einfach eingefügt.
2011-08-10T08:43:50 lousekDer letzte Teil ist wieder klar:
Alle Einträge in der DB, welche verwaist sind (status = 0), werden gelöscht.
2011-08-10T08:43:50 lousekBei mir werden doch viel weniger SQL-Queries benötigt?
2011-08-10T08:43:50 lousekOder kann es sein, dass ich (trotz nachschauen auf CPAN) das mit dem begin_working() und commit() nicht ganz verstanden habe? :)