Or ... a 100% perl solution,
directories output terminated by ASCII null:
#!/usr/bin/perl
$^W=1;
use strict;
use File::Find;
my $parentdir=undef;
sub process {
if(-d _){
if(!defined($parentdir)){
print($File::Find::name,"\0",);
}else{
print($File::Find::name,"\0",) if $parentdir ne $File::Find::name;
};
$parentdir=$File::Find::dir;
};
};
for my $arg (@ARGV){
$parentdir=undef;
find({ wanted => \&process, bydepth => 1, }, ($arg));
};
And, bit of explanation on how that works.
First of all, perl's File::Find has a capability rather like
find(1)'s -depth option, by setting its bydepth option to 1 or using
finddepth. Essentially by using such, it will always process the
contents of a directory before processing the directory itself.
Then, we also make some logical presumptions about how perl's File::Find
goes about implementing that. It's not merely processing contents of
directory before processing directory itself, but is a bit more strict
than that. Let's say we have a directory structure like this - where
all named path elements are of type directory:
d0/d1
d0/d2/d3
d0/d2/d4
We can very reasonably presume, that not only will it process d3 and d4
before processing d2, but also that it won't process d1 between
processing d3 and d4, and processing d2. Why? Recursion, "of course".
How does one think its implemented in perl? It has to read the
contents of a directory to determine if it has any subdirectories
within, and if so, it must descend and process those first. It would
"of course" use recursion on that, and eventually backtrack. Hence we
have somewhat more predictability of the order of processing of
directories beyond it merely processing entries in directory before
processing directory itself. Notably that for a given ancestor
directory, all descendants will be processed before that ancestor, and
for any directory which is not that ancestor or descendant thereof, it
will either be processed before that ancestor and its descendants are
processed, or it will be processed after that ancestor and its
descendants are processed. We can further logically break that down.
The first directory we process must be a "leaf" directory - because any
descendant directories would be processed earlier. We track the
immediate parent of this leaf. Next time we're processing a directory,
we check that it's not same as parent we tracked with our last
directory visit. If it matches, it's a parent and we don't output it,
if it doesn't match, it must be a leaf directory - we know this due to
the restrictions on the ordering used in the recursion to process the
directories - so we only have to track that one bit of state - perl
tracks everything else needed inherently in the recursion of doing
depth first processing. That's basically it. And doing this in perl,
we have much efficiency, by avoiding need for any fork(2) or exec(3)
calls or the like, we avoid any need to sort and related storage, and
we only need track pretty minimal state information - other than that
which perl tracks for us in its recursion - which is close to what
minimally would need to be tracked anyway.
One other key bit here too, use of _ - that's effectively a "magic"
filehandle in perl - we can lstat it to check if it's a directory. But
what file does it refer to? The last on which an lstat was done. And,
due to how we can reasonably presume File::Find to work, we can safely
presume that when we go to examine _, it's already cached lstat data
for the file (of whatever type) we're about to process. So, using -d _
allows us to test against the existing cached lstat data (to check if
it's a directory), without need to do another actual lstat(2) system
call. Anyway, we're able to leverage those bits of how File::Find
works in perl to make our task at hand particularly efficient ... and
if we weren't certain about those behaviors, we could examine the
source code ... but those are very reasonable presumptions based upon
how perl works in general, and what File::Find needs to do, and how it
would implement it reasonably efficiently, and particularly also how it
would need to reasonably efficiently implement its bydepth/finddepth
functionality.