Thread CSV-Datei ohne Modul schreiben (15 answers)
Opened by Stuermchen at 2011-07-12 09:07

topeg
 2011-07-12 14:12
#150322 #150322
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Nachfolgend drei Ansätze wie man es machen kann.

Ich habe es für Linux-Systeme umgesetzt, soll es auch unter Windows funktionieren so muss die Bestimmung der Gruppen/Nutzer anders ablaufen.

Der Erste ist Speicher schonend,
der Zweite ist schnell,
und der Dritte ist objektorientiert.

Die Herangehensweise ist grundsätzlich:
  • gehe die die Verzeichnisse durch trage alle User in eine Liste ein
  • gehe die Liste der User durch und stelle fest ob sie in der Gruppe des Verzeichnisses sind


Der Nachteil, wenn man keine Daten vorhält ist, dass man die Daten immer wieder suchen muss. Hier werden die Dateien "/etc/passwd" und "/etc/group" für jedes Verzeichnis und jeden User erneut geöffnet, das kostet jedes mal Zeit.
more (18.7kb):
Code (perl): (dl )
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
#!/usr/bin/perl
use strict;
use warnings;

# langsam aber speicherschonend

my @dirs=qw( /home/shared /home/topeg /home/test /var/log);

# erste zeile
print ";".join(';',@dirs)."\n";

my %user_list;

# alle User finden
for my $dir (@dirs)
{
  for my $user (get_user_for_dir($dir))
  { $user_list{$user}=1; }
}

# alle user Ausgeben
for my $user (sort(keys(%user_list)))
{
  print "$user;";

  # alle verzeichnisse durch gehen
  # und 1/0 setzen für diesen User
  my @list;
  for my $pos (0..$#dirs)
  {
    my $ok=0;
    # alle user für das verzeichnis durchgehen
    for my $ruser (get_user_for_dir($dirs[$pos]))
    {
      if($ruser eq $user)
      {
        $ok=1;
        last;
      }
    }
    $list[$pos]=$ok;
  }
  print join(';',@list)."\n";
}
########################################################################

# user für ein Verzeichnis
sub get_user_for_dir
{
  my $dir=shift;
  my @user;

  my $gid=get_gid_for_dir($dir);
  @user=get_user_for_gid($gid) if(defined($gid));

  return @user;
}

# gid für ein verzeichnis
sub get_gid_for_dir
{
  my $dir=shift;
  my $gid=undef;

  $gid=(stat($dir))[5] if(-d $dir);

  return $gid;
}

# alle user zu einer gid
sub get_user_for_gid
{
  my $gid=shift;
  my $gfile='/etc/group';
  my $ufile='/etc/passwd';

  my @user;

  if(-e $gfile && open(my $fh, '<', $ufile))
  {
    while(my $line=<$fh>)
    {
      chomp($line);
      my ($user,undef,undef,$fgid)=split(/:/,$line);
      if($fgid == $gid)
      { push(@user,$user); }
    }
    close($fh);directory
  }

  if(-e $gfile && open(my $fh, '<', $gfile))
  {
    while(my $line=<$fh>)
    {
      chomp($line);
      my (undef,undef,$fgid,$user)=split(/:/,$line);
      if($gid == $fgid)
      {
        push(@user,split(/,/,$user));
        last;
      }
    }
    close($fh);
  }

  return @user;
}


Hier werden alle Daten, wenn möglich nur einmal gelesen. Das füllt natürlich den Speicher, beschleunigt die Zugriffe auf die Daten aber enorm.
more (16.1kb):
Code (perl): (dl )
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
#!/usr/bin/perl
use strict;
use warnings;

# schnell und speicherhungrig

my @dirs=qw( /home/shared /home/topeg /home/test /var/log);

# erste zeile
print ";".join(';',@dirs)."\n";

# alle vorhandenen groups lesen
my %gid_list=read_groups();

# alle User finden
my %user_list;
for my $pos (0..$#dirs)
{
  my $dir=$dirs[$pos];
  my $gid=get_gid_for_dir($dir);
  if(defined($gid) && exists($gid_list{$gid}))
  {
    for my $user (@{$gid_list{$gid}})
    {
      $user_list{$user}->[$pos]=1;
    }
  }
}

# alle user Ausgeben
for my $user (sort(keys(%user_list)))
{
  print "$user;";

  # iste lesen und sicher stellen, dass alle Zeilen geholt werden
  my @list=@{$user_list{$user}}[0..$#dirs];

  # werte auf 1/0 setzen
  map{$_=$_?1:0}@list;

  print join(';',@list)."\n";
}

########################################################################

# gid für ein verzeichnis
sub get_gid_for_dir
{
  my $dir=shift;
  my $gid=undef;

  $gid=(stat($dir))[5] if(-d $dir);

  return $gid;
}

# alle user zu einer gid
sub read_groups
{
  my $gfile='/etc/group';
  my $ufile='/etc/passwd';

  my %gid_user;

  if(-e $gfile && open(my $fh, '<', $ufile))
  {
    while(my $line=<$fh>)
    {
      chomp($line);
      my ($user,undef,$uid,$gid)=split(/:/,$line);
      push(@{$gid_user{$gid}},$user);
    }
    close($fh);
  }

  if(-e $gfile && open(my $fh, '<', $gfile))
  {
    while(my $line=<$fh>)
    {
      chomp($line);
      my ($gname,undef,$gid,$user)=split(/:/,$line);
      push(@{$gid_user{$gid}},split(/,/,$user));
    }
    close($fh);
  }

  return %gid_user;
}



Der Vorteil bei der Objektorientierung ist, dass man die Objekte leicht erweitern kann, ohne alle umschreiben zu müssen. Da auch Code gebündelt wird, ist es leichter Fehler zu beheben.

Ich nutze hier drei Klassen:
  • directory
  • user
  • gids

directory ist zur Verwaltung eines Verzeichnisses gedacht. Über das Objekt kann man User finden, den pfad lesen, etc.
user verwaltet einen Nutzer.
gids veraltet die Groupids und die dazugehörigen user Alle Dateizugriffe werden gekapselt und 'versteckt'. Als Feature wird die Liste der Gruppen immer dann aktualisiert, wenn sich etwas in der Datei geändert hat.

more (37.7kb):
Code (perl): (dl )
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
#!/usr/bin/perl
use strict;
use warnings;

# objekt orientiert

my @dirs=map{ directory->new($_) }qw( /home/shared /home/topeg /home/test /var/log);

# erste zeile
print ';'.join(';', map{$_->path()}@dirs )."\n";

# alle user finden
my %users;
for my $dir (@dirs)
{
  for($dir->user_list())
  { $users{$_->name()}=$_; }
}

# alle user Ausgeben
for(sort(keys(%users)))
{
  my $user=$users{$_};
  my @list=map{ $user->has_dir($_) }@dirs;
  print $user->name().";".join(';',@list)."\n";
}
########################################################################
########################################################################
########################################################################
package directory;

my $gids;

sub new
{
  my $class=shift();
  my $dir=shift;

  $gids=gids->new() unless($gids);

  return undef unless(-d $dir);
  bless({dir=>$dir},$class);
}

sub path { shift()->{dir}; }

sub gid
{
  my $self=shift;
  return (stat($self->path()))[5] if(-d $self->path());
  return undef;
}

sub user_list
{
  my $self=shift;
  return $gids->user_list($self->gid());
}

sub has_user
{
  my $self=shift;
  my $user=shift;
  return 0 unless($user);
  return 0 unless($user->isa('user'));
  for($self->user_list())
  { return 1 if($_->name() eq $user->name()); }
  return 0;
}

########################################################################
package user;

sub new
{
  my $class=shift;
  my $name=shift;
  bless({name=>$name},$class);
}

sub name { shift()->{name}; }

sub has_dir
{
  my $self=shift;
  my $dir=shift;
  return 0 unless $dir->isa('directory');
  my $path=$dir->path();

  unless(exists($self->{dirs}->{$path}))
  { $self->{dirs}->{$path}=$dir->has_user($self); }

  return $self->{dirs}->{$path};
}

########################################################################
package gids;

sub new
{
  my $class=shift();
  my $gfile=shift() || '/etc/group';
  my $ufile=shift() || '/etc/passwd';
  return bless({ufile=>$ufile, gfile=>$gfile, user=>{}},$class);
}

sub user_list
{
  my $self=shift;
  my $gid=shift;
  $self->_read_groups();
  return () unless(defined($gid));
  return () unless(exists($self->{gids}->{$gid}));
  return @{$self->{gids}->{$gid}};
}

sub user
{
  my $self=shift;
  my $name=shift;
  unless($self->{user}->{$name})
  { $self->{user}->{$name}=user->new($name); }
  return $self->{user}->{$name};
}

# alle user zu einer gid
sub _read_groups
{
  my $self=shift;

  # update wenn nötig
  if(!$self->{update_g} || !$self->{update_u} || !$self->{gids} ||
     $self->{update_g} < (stat($self->{gfile}))[9] || $self->{update_u} < (stat($self->{ufile}))[9] || ref($self->{gids}) ne 'HASH')
  {

    my %gid_user;
    if(-e $self->{ufile} && open(my $fh, '<', $self->{ufile}))
    {
      while(my $line=<$fh>)
      {
        chomp($line);
        my ($user,undef,$uid,$gid)=split(/:/,$line);
        $user=$self->user($user);
        push(@{$gid_user{$gid}},$user);
      }
      close($fh);

      $self->{update_u} = (stat($self->{ufile}))[9]
    }

    if(-e $self->{gfile} && open(my $fh, '<', $self->{gfile}))
    {
      while(my $line=<$fh>)
      {
        chomp($line);
        my ($gname,undef,$gid,$users)=split(/:/,$line);
        for my $name (split(/,/,$users))
        {
          my $user=$self->user($name);
          push(@{$gid_user{$gid}}, $user);
        }
      }
      close($fh);

      $self->{update_g} = (stat($self->{gfile}))[9];
    }

    $self->{gids}=\%gid_user;
  }
}

View full thread CSV-Datei ohne Modul schreiben