chown recursively

T

Tim O'Donovan

Quick newbie question! Could someone please point out how to use chown()
on directories recursively.

But mostly I'd like to know if there are actually any benefits over using:

system("chown -R $uid:$gid $dir");


Thanks very much!

Tim
 
L

Lars Eighner

In our last episode,
the lovely and talented Tim O'Donovan
broadcast on comp.lang.perl.misc:
Quick newbie question! Could someone please point out how to use chown()
on directories recursively.
But mostly I'd like to know if there are actually any benefits over using:
system("chown -R $uid:$gid $dir");

This is really a unix question, not a perl question. A word of
warning, though: beware of -R and .* -- it will recurse upward
because .. matches .* .
 
T

Tim O'Donovan

Well my question was actually concerning whether using a Perl function
such as chown() had any benefits over using an equivalent system()
function to achieve the same the results.

Tim
 
C

Christopher Nehren

Quick newbie question! Could someone please point out how to use chown()
on directories recursively.

Perhaps a bit of File::Find?
But mostly I'd like to know if there are actually any benefits over using:

system("chown -R $uid:$gid $dir");

Who says chown exists on the target system? Who says it's in your path?
It's generally a bad idea to "shell out" to external programs when you
can do what you need to do with Perl.

Best Regards,
Christopher Nehren
 
K

Kjetil Skotheim

Perhaps a bit of File::Find?


Who says chown exists on the target system? Who says it's in your path?
It's generally a bad idea to "shell out" to external programs when you
can do what you need to do with Perl.


I agree that GENERALLY this is a bad idea. But since chmod in itself
is very unix'ish, isn't it?, I think system should be permitted here.
....or if not permitted, I'd do a chmod to permit it...(uh)
 
T

Tad McClellan

Christopher Nehren said:
On 2005-06-09, Tim O'Donovan scribbled these curious markings:

[snip doing it in native Perl]

Who says chown exists on the target system?


ie. portability between different machines.

Who says it's in your path?


ie. portability between environments, even on *the same* machine.

It's generally a bad idea to "shell out" to external programs when you
can do what you need to do with Perl.


I tell students:

Doing it in native Perl will be safer, faster and more portable
than "shelling out".

Followed by some weasel wording about how that isn't an absolute,
but that it applies in the vast majority of cases.

Shelling out to Mathematica would very likely be preferrable to
getting the same thing done in native Perl for instance. :)
 
T

Tim O'Donovan

Thanks for all your informative replies, much appreciated. From now on
I'll be replacing 'shell outs' with the appropriate Perl, where applicable.

So I can assume that using:

open(FIND, "find $path -lname '/home/$username/*' |");
@links = <FIND>;
close(FIND);

to find all the symbolic links in the given path to files in a users
home directory is also not the most resourceful method?!

I have some rethinking to do... :)


Thanks again,
Tim
 
T

Tad McClellan

Tim O'Donovan said:
From now on
I'll be replacing 'shell outs' with the appropriate Perl, where applicable.
open(FIND, "find $path -lname '/home/$username/*' |");


Pipe opens *are* "shelling out".

to find all the symbolic links in the given path to files in a users
home directory is also not the most resourceful method?!

I have some rethinking to do... :)


And some reading:

perldoc File::Find

perldoc -f -X
 
J

Jürgen Exner

Tim said:
Quick newbie question! Could someone please point out how to use
chown() on directories recursively.

Sounds like a job for "use File::Find;".
But mostly I'd like to know if there are actually any benefits over
using:
system("chown -R $uid:$gid $dir");

Of course, there are significant benefits. By using a Perl standard function
you are avoiding to fork an external process and to call an external program
that may or may not exist.
In short you are writing portable code.

jue
 
J

Jürgen Exner

Kjetil said:
I agree that GENERALLY this is a bad idea. But since chmod in itself
is very unix'ish, isn't it?, I think system should be permitted here.
...or if not permitted, I'd do a chmod to permit it...(uh)

Where do you find chown on a Windows PC, on a Mac, or a any of the dozen and
dozen OSs that are supported by Perl?

jue
 
J

Jürgen Exner

Tim said:
open(FIND, "find $path -lname '/home/$username/*' |");
@links = <FIND>;
close(FIND);

to find all the symbolic links in the given path to files in a users
home directory is also not the most resourceful method?!

No, not exactly.
See "perldoc -f -l" for a better way.

jue
 
C

Christopher Nehren

For example, if you decide to shell out to an external chown(1) command,
it will work on Linux, *BSD, Solaris, and even Mac OS X, and several
others...

.... though it may fail if you use syntax supported in your programming
environment's chown but not in your deployment environment's chown. For
example, GNU chown accepts '.' as a separator for user and group, which
is a violation of POSIX (though at least the documentation tells you
this). FreeBSD's chown follows POSIX and explicitly disallows this. All
the more reason to use Perl when possible.

Best Regards,
Christopher Nehren
 
T

Tim O'Donovan

Well, if the directory is large, I bet shelling out the chown is *faster*
than doing it in Perl. The system call doesn't handle the -R functionality,
and I bet the C-implementation of traversing a directory tree in chown is
faster than most, if not every, Perl solution using File::Find.

Of course, the strongest argument is: programmer time is more important
than CPU time. The time to program out

system "chown -R $uid:$gid $dir";

utterly dwarves the overhead of starting another process. (And note that
unless '$uid', '$gid' and '$dir' contain characters that are special to
shell, no shell will be started by perl - perl will call chown directly).


Thanks for the alternative angle, excellent stuff. I'm considering using
the original system() function now because of what you said about speed.
This is part of a background script on a webserving machine that takes
instructions from a file written to by a CGI script so the time each
request takes to execute is of the utmost importance as we want to keep
the user waiting for as little as possible for the result of the
request. I've yet to find a way to use the sleep() function to sleep for
less than a second (although I'm sure to find a module that will do
this), so even though using the system() function might only reduce the
time by a millisecond, this could cause the CGI script to sleep for a
second more than it really needs to. Well, just under a second than it
*needs* to but...

unless '$uid', '$gid' and '$dir' contain characters that are special to shell...

This is also good to know as it will only ever be passed variables from
the calling CGI script like so:

system("chown -R 1100:80 /home/username/public_html");

The $uid value is taken directly from a passwd file, $gid is a constant
of 80 and if $username is not alphanumeric the CGI script will return an
error.



Tim
 
T

Tim O'Donovan

Abigail said:
Indeed. I'd write that as:

@links = `find $path -lname '/home/$username/*'`;

I have been getting rather confused trying to find a way to achieve this
using the -l file test as a few people have suggested. From the perldoc
it describes this as finding the symbolic links to a file that *don't*
resolve; I need to find links that *do*. I will probably now use your
suggestion to achieve this due to my frivolous attempts at using !-l !


Tim
 
B

Brian McCauley

Abigail said:
Of course, the strongest argument is: programmer time is more important
than CPU time. The time to program out

system "chown -R $uid:$gid $dir";

utterly dwarves the overhead of starting another process. (And note that
unless '$uid', '$gid' and '$dir' contain characters that are special to
shell, no shell will be started by perl - perl will call chown directly).

But even the briefest consideration of the question of whether or not
$dir may contain shell meta character takes more programmer time than
typing the extra punctuation to avoid this being an issue.

system 'chown','-R',"$uid:$gid",$dir;
 
J

Joe Smith

Tim said:
For example, if you decide to shell out to an external chown(1) command,
it will work on Linux, *BSD, Solaris, and even Mac OS X, and several
others... but fail on Win32.

Works just fine in Win32 under cygwin.
-Joe
 
J

Joe Smith

Tim said:
So I can assume that using:

open(FIND, "find $path -lname '/home/$username/*' |");
@links = <FIND>;
close(FIND);

to find all the symbolic links in the given path to files in a users
home directory is also not the most resourceful method?!

You could capture the output from
find2perl PATH -l -name '/home/USER/*'
and modify that to do the -lname operation.
Just change
/^\/home\/jms\z/s &&
print("$name\n");
to
readlink $_ =~ m%/home/USER/.*% &&
push @links,$name;

The find2perl program that comes with the perl installation doesn't
do everything that GNU find does, but it is a good starting point.
-Joe
 
M

Michael Zawrotny

[ snip ]
Thanks for the alternative angle, excellent stuff. I'm considering using
the original system() function now because of what you said about speed.

Don't worry about the speed. I used the following setup on my
workstation (3 GHz P4 running Ubuntu linux):

directory with 10 sub-directories
each with 10 sub-sub-directories (100 at this level)
each with 10 sub-sub-sub-directories (1000 at this level)
each with 10 files (10000 at this level)

chown -R took about 0.05 seconds, calling Perl's builtin chown on each
file/directory took 0.13 seconds, and building a large list of
files/directories and feeding that to one call to Perl's chown took
0.15 seconds.

The thrown-together-in-a-few-minutes perl with path, uids, and gids
hard coded is:

#!/usr/bin/perl

use warnings;
use strict;
use File::Find;

my @list;

sub wanted1 {
chown(1000, 1588, $_);
}

sub wanted2 {
push @list, $File::Find::name;
}

# dirs is the name of the directory to recursively chown
if ( $ARGV[0] == 1 ) {
find(\&wanted1, 'dirs');
} elsif ( $ARGV[0] == 2 ) {
find(\&wanted2, 'dirs');
chown(1000, 1588, @list);
}

__END__


Mike
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top