You can run find
on the result of another find
, but not with that syntax.
find / -maxdepth 1 -type d -name "/[A-Z]*" | xargs find -type d -name "X"
First, you can't use xargs
like that on the output of find
for any command, find
or otherwise, unless none of the file names it outputs contain any whitespace characters, quotes, or backslashes (or non-characters with some implementations).
You can only use xargs
with the non-standard -0
option on the output of find -print0
(-print0
also non-standard) if you want to be able to process arbitrary files. The output of find -print
itself is simply not post processable (see Why is looping over find's output bad practice?).
Also, here, xargs
will append the file paths to the second find
command, and put them after the predicates that make up the filtering criteria. The list of files find
is to operate on must be given before any predicate.
More generally, using xargs
on the output of find
is rarely needed, as find
has its own builtin (and more reliable and often more efficient) support to run commands on the files it finds with its -exec
/-ok
(and for some -execdir
/-okdir
) predicates.
But like with xargs
, you need to make sure the list of files for the second find
comes before the predicates, so it would have to be:
find / -maxdepth 1 -name '[[:upper:]]*' -type d -exec sh -c '
exec find "$@" -name X -type d' sh {} +
Where we use the -exec cmd {} +
form of -exec
which like xargs
passes as many possible paths as possible to cmd
but also can only pass them at the end. sh
is used to move those in the right place for the second find
.
Also note that -name
matches on the name of the file, not its full path (for which you need -path
). So we need [[:upper:]]*
not /[[:upper:]]*
(nor [A-Z]*
where what it matches on is often rather random depending on the locale) to match on filenames that start with an uppercase letter.
With the next version of GNU find
(or its current development version), you'd also be able to do:
find / -maxdepth 1 -name '[[:upper:]]*' -type d -print0 |
find -files0-from - -name X -type d
Here, you could do it whole with one find
invocation though:
find / -path '/[![:upper:]]*' -prune -o -name X -type d -print
Where we tell find
to prune the branches of the tree that start with directories in /
whose name starts by any character other than an uppercase letter before looking for directory named X.
Note that with some find
implementations on some systems (including GNU find
on GNU systems), *
may fail to match parts of file names that are not valid text in the current locale.
For example, the command above would find /stéphane/X
even though s
is not an uppercase letter if that é
was encoded in iso8859-1 and the current locale was using UTF-8 as its charmap (where the 0xe9 byte there could not be decoded into a character so *
couldn't match across it), and the first command would fail to find /Stéphane/X
for the same reason.
zsh
globs don't have this kind of problem as the treat each byte that can't be decoded into a character as if it was an undefined character, so you could do:
print -rC1 /[[:upper:]]*/**/X(ND/)
Or as a slight optimisation if you don't need the list to be o
rdered:
print -rC1 /[[:upper:]]*/**/X(ND/oN)
Note that it would include /SymLink/.../X
dirs. To avoid that:
(){print -rC1 $^@/**/X(ND/oN)} /[[:upper:]]*(N/oN)
or:
print -rC1 /[[:upper:]]*(N/oNe['reply=($REPLY/**/X(ND/oN)'])
Which are like the two stage find
approaches: find the directories whose name starts with an uppercase letter in one glob and then all the X dirs in them as separate globs.