is there a better way to mkdir?

W

wana

I use mkdir in a program which recursively searches through a
directory searching for files of a particular type and recreating the
directory structure elsewhere and the files (which happen to be images
which get manipulated by ImageMagick along the way).

In recreating the directory structure, I sometimes come across the
error:

No such file or directory

which is due to trying to create multiple directory levels at once
which is not allowed. While this probably is a problem in my program
logic (that is another whole issue, but I didn't want to ask too much
at once), I was wondering, why can't I create a whole path at once
like:

mkdir mouse/cat/dog

where none of the three directories exist yet.

This is what I came up with, but I thought there might be another way
or a better way to do it.

sub SuperMkdir
{
my $path = shift;
my $hold = $path;
while (mkdir($path) == 0)
{
do
{
$path =~ s/[^\/]+\/*$//;
}
while (mkdir($path) == 0);
$path = $hold;
}
}
 
R

Richard Gration

I was wondering, why can't I create a whole path at once
like:

mkdir mouse/cat/dog

where none of the three directories exist yet.

This is what I came up with, but I thought there might be another way
or a better way to do it.

Dunno about a perl solution but unix mkdir supports a -p flag for this, so

system 'mkdir -p jerry/tom/spike'

will succeed regardless of whether jerry or tom already exist.

Rich
 
A

Anno Siegel

wana said:
I use mkdir in a program which recursively searches through a
directory searching for files of a particular type and recreating the
directory structure elsewhere and the files (which happen to be images
which get manipulated by ImageMagick along the way).

In recreating the directory structure, I sometimes come across the
error:

No such file or directory

which is due to trying to create multiple directory levels at once
which is not allowed. While this probably is a problem in my program
logic (that is another whole issue, but I didn't want to ask too much
at once), I was wondering, why can't I create a whole path at once
like:

mkdir mouse/cat/dog

where none of the three directories exist yet.

This is what I came up with, but I thought there might be another way
or a better way to do it.

sub SuperMkdir
{
my $path = shift;
my $hold = $path;
while (mkdir($path) == 0)
{
do
{
$path =~ s/[^\/]+\/*$//;
}
while (mkdir($path) == 0);
^^
That is wrong. mkdir returns true on success.
$path = $hold;
}
}

Well, I guess it might work, but it is more convoluted than it has to be.
I wouldn't use pattern substitution to extract the partial path names,
I'd split on the directory separator and build the paths from the parts.
That can be done in a standard for-loop.

my $dir;
for ( split m{/}, $path ) {
$dir .= '/' if length $dir;
$dir .= $_;
mkdir $dir or last;
}

or even

my @parts = split m{/}, $dir;
mkdir join( '/', @$_) for map [ @parts[ 0 .. $_]], 0 .. $#parts;

Anno
 
A

Anno Siegel

Richard Gration said:
Dunno about a perl solution but unix mkdir supports a -p flag for this, so

There is no such thing as "unix mkdir". Most modern brands of unix
appear to have the -p flag for mkdir, but I wouldn't bet my portability
on it.
system 'mkdir -p jerry/tom/spike'

will succeed regardless of whether jerry or tom already exist.

On which systems?

Anno
 
R

Richard Gration

Richard Gration <[email protected]> wrote in comp.lang.perl.misc:
There is no such thing as "unix mkdir". Most modern brands of unix
appear to have the -p flag for mkdir, but I wouldn't bet my portability
on it.


On which systems?

You're right, I should be more specific. Linux mkdir will behave as I
described.

R
 
M

Michele Dondi

I use mkdir in a program which recursively searches through a
directory searching for files of a particular type and recreating the
directory structure elsewhere and the files (which happen to be images

Well, I tend to avoid using perl just to launch a bunch of system()
cmds as many people does (but then they do much worse things like
calling grep, sed, etc. in those system()s), and I've hardly used any
until recently when I had to quickly hack up a script that does
something very similar to what you need, for which neither a pure *sh
script nor a "pure Perl one" (in a loose, but hopefully clear, sense)
would satisfy my laziness (you do remember that virtue called
'laziness', don't you?), so I used an external 'mkdir -p' which is
working great for me. It won't be available on all systems though.


Michele
 
W

wana

sub SuperMkdir
{
my $path = shift;
my $hold = $path;
while (mkdir($path) == 0)
{
do
{
$path =~ s/[^\/]+\/*$//;
}
while (mkdir($path) == 0);
^^
That is wrong. mkdir returns true on success.

I am actually testing for failure. Each time mkdir fails, I chop of
the end of the path and try again until I succeed. The outer loop
repeats this process over and over until the whole path is
successfully created and the loops are exited. I tested it before
posting on a few examples.
$path = $hold;
}
}

Well, I guess it might work, but it is more convoluted than it has to be.
I wouldn't use pattern substitution to extract the partial path names,
I'd split on the directory separator and build the paths from the parts.
That can be done in a standard for-loop.

my $dir;
for ( split m{/}, $path ) {
$dir .= '/' if length $dir;
$dir .= $_;
mkdir $dir or last;
}

or even

my @parts = split m{/}, $dir;
mkdir join( '/', @$_) for map [ @parts[ 0 .. $_]], 0 .. $#parts;

Anno

I like your way. I was thinking that I had to account for different
ways the path might appear:

//dog/cat//mouse/
dog/cat/mouse
../dog/cat/mouse//

The double //'s might come up as a side effect of how a program puts
paths together. Split should still work because you will just get an
empty string in the array which will not change anything.

wana
 
G

Gary E. Ansok

I use mkdir in a program which recursively searches through a
directory searching for files of a particular type and recreating the
directory structure elsewhere and the files (which happen to be images
which get manipulated by ImageMagick along the way).

In recreating the directory structure, I sometimes come across the
error:

No such file or directory

which is due to trying to create multiple directory levels at once
which is not allowed. While this probably is a problem in my program
logic (that is another whole issue, but I didn't want to ask too much
at once), I was wondering, why can't I create a whole path at once
like:

mkdir mouse/cat/dog

where none of the three directories exist yet.

Have you tried the mkpath() function in the File::path module?

use File::path;

mkpath('mouse/cat/dog', 0, 0755);

(change the 0 to 1 and it will print out each directory it creates.)

This is a standard module, so you should have it available already.

-- Gary Ansok
 
T

Tintin

Gary E. Ansok said:
Have you tried the mkpath() function in the File::path module?

use File::path;

mkpath('mouse/cat/dog', 0, 0755);

(change the 0 to 1 and it will print out each directory it creates.)

This is a standard module, so you should have it available already.

and I'm surprised that you're the only person (so far), that has suggested
this obvious answer.

I'm surprised the regulars went for their roll-their-own methods.
 
A

Anno Siegel

Abigail said:
Anno Siegel ([email protected]) wrote on MMMMLXII
September MCMXCIII in <URL::) > On Thu, 14 Oct 2004 08:56:27 -0700, wana wrote:
:) >
:) > > I was wondering, why can't I create a whole path at once
:) > > like:
:) > >
:) > > mkdir mouse/cat/dog
:) > >
:) > > where none of the three directories exist yet.
:) > >
:) > > This is what I came up with, but I thought there might be another way
:) > > or a better way to do it.
:) >
:) > Dunno about a perl solution but unix mkdir supports a -p flag for this, so
:)
:) There is no such thing as "unix mkdir". Most modern brands of unix
:) appear to have the -p flag for mkdir, but I wouldn't bet my portability
:) on it.

The POSIX requirement for support of options of utilities is often not
broad. But it does require -p for mkdir.

All I knew when I wrote my reply was that "-p" wasn't always there
for mkdir. I remmber thinking "Ah, finally..." when it appeared.
Portability is overrated anyway. I claim that the fast majority of the
Perl scripts that are written will only run on one system anyway.

True for most programs written by yourself for yourself. Not true
for publishable code, as for CPAN.

Anno
 
A

Anno Siegel

wana said:
sub SuperMkdir
{
my $path = shift;
my $hold = $path;
while (mkdir($path) == 0)
{
do
{
$path =~ s/[^\/]+\/*$//;
}
while (mkdir($path) == 0);
^^
That is wrong. mkdir returns true on success.

I am actually testing for failure. Each time mkdir fails, I chop of
the end of the path and try again until I succeed. The outer loop
repeats this process over and over until the whole path is
successfully created and the loops are exited. I tested it before
posting on a few examples.

Ah... sorry. I admit I didn't really follow the logic of your program.
All I saw was a big old regex and a "do {} while..." loop and thought,
we don't need that shit... :)
$path = $hold;
}
}

Well, I guess it might work, but it is more convoluted than it has to be.
I wouldn't use pattern substitution to extract the partial path names,
I'd split on the directory separator and build the paths from the parts.
That can be done in a standard for-loop.

my $dir;
for ( split m{/}, $path ) {
$dir .= '/' if length $dir;
$dir .= $_;
mkdir $dir or last;
}

or even

my @parts = split m{/}, $dir;
mkdir join( '/', @$_) for map [ @parts[ 0 .. $_]], 0 .. $#parts;

Anno

I like your way. I was thinking that I had to account for different
ways the path might appear:

//dog/cat//mouse/
dog/cat/mouse
./dog/cat/mouse//

The double //'s might come up as a side effect of how a program puts
paths together. Split should still work because you will just get an
empty string in the array which will not change anything.

Normalizing the path is another issue. I would treat that logically
instead of letting the file system detect that parts of the path
have already been created. If it's only multiple slashes, split()
can be modified accordingly. Things like "dog/cat/../cat/mouse"
deserve separate processing.

Speaking of normalization, if the $path is always terminated with
"/", your original regex approach becomes attractive again:

for ( $path ) {
$_ .= "/" unless m{/$};
mkdir substr( $_, 0, pos) while m{.*?/}g;
}

Oh, and ... yes, I'm aware that there are standard modules that
do this. It may still be illuminating to explore code variants.

Anno
 
A

Anno Siegel

Abigail said:
Anno Siegel ([email protected]) wrote on MMMMLXII
September MCMXCIII in <URL::) > On Thu, 14 Oct 2004 08:56:27 -0700, wana wrote:
:) >
:) > > I was wondering, why can't I create a whole path at once
:) > > like:
:) > >
:) > > mkdir mouse/cat/dog
:) > >
:) > > where none of the three directories exist yet.
:) > >
:) > > This is what I came up with, but I thought there might be another way
:) > > or a better way to do it.
:) >
:) > Dunno about a perl solution but unix mkdir supports a -p flag for this, so
:)
:) There is no such thing as "unix mkdir". Most modern brands of unix
:) appear to have the -p flag for mkdir, but I wouldn't bet my portability
:) on it.

The POSIX requirement for support of options of utilities is often not
broad. But it does require -p for mkdir.

All I knew when I wrote my reply was that "-p" wasn't always there
for mkdir. I remember thinking "Ah, finally..." when it appeared.
Portability is overrated anyway. I claim that the fast majority of the
Perl scripts that are written will only run on one system anyway.

True for most programs written by yourself for yourself. Not true
for publishable code, as for CPAN.

Anno
 
M

Michele Dondi

I use 'system' or 'open "$cmd |"' often, even if there are Perl modules
available to do the same. I've never had a problem with using 'system

Me too!
mkdir => "-p", $dir', and it sure beats writing out the loop yourself. I

Me neither, as of the other post.
always use 'system [rs]?cp|rsync' to copy files, as File::Copy is just

Well I've rarely enough had to actually copy files in Perl...
broken (and doesn't have a tenth of the functionality of 'cp'). I can't
be bothered to learn the semantics of File::Find - forking out to 'find'
always does the trick for me.

Well, File::Find is a module, for one, that I rapidly get to
appreciate, not it's semantics is deeply etched in my cortex! At least
for this one no forking to 'find'! ;-)
Perl is a glue language, and its ability to call other programs is a
feature - a feature I treasure. And I don't have any religious feelings

Me neither!
that urge me to write "pure Perl". As for portability, all my portability
problems arise from Perl itself, not the tools I fork out to.

I think you're right. However my cmt was about thos people doing thing
like

my @list=`ls -1 *.txt | grep -ve '$foo' | xargs cat | sed ...`

I hope YOU don't do such things. In Perl, that is: shell programming
is quite another thing!

I must admit I've probably been over-suspicious about system() & C. in
the past. (But I like '-|' and '|-'-open()s very much!)


Michele
 

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,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top