Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I'm trying to figure out if there is a way to get the UNIX command tree to display only directories that match a specific pattern.

% tree -d tstdir -P '*qm*' -L 1
tstdir
|-- d1
|-- d2
|-- qm1
|-- qm2
`-- qm3

5 directories

The man page shows this bit about the switch.

-P pattern List only those files that match the wild-card pattern. Note: you must use the -a option to also consider those files beginning with a dot .' for matching. Valid wildcard operators are*' (any zero or more characters), ?' (any single character),[...]' (any single character listed between brackets (optional - (dash) for character range may be used: ex: [A-Z]), and [^...]' (any single character not listed in brackets) and|' sepa‐ rates alternate patterns.

I'm assuming that the bit about ...List only those files... is the issue. Am I correct in my interpretation that this switch will only pattern match on files and NOT directories?

EDIT #1

@f-hauri looks to have the best reason as to why this doesn't work the way one would think from the switches available in the tree man page. I missed this bit in the BUGS section.

BUGS
   Tree  does not prune "empty" directories when the -P and -I options are
   used.  Tree prints directories as it comes to them, so  cannot  accumu‐
   late  information  on files and directories beneath the directory it is
   printing.

Given this limitation it looks like tree isn't the best way to accomplish an inclusive filtered list, but a exclusive filtered list would be an alternative way using the -I switch.

In lieu of this it would look like either shell wildcards, the find command, or a Perl script would be a more appropriate way to accomplish this. See @f-hauri's fine answer for some of these alternative methods.

share|improve this question

migrated from stackoverflow.com Jan 17 '13 at 15:02

This question came from our site for professional and enthusiast programmers.

    
It needs to show the directories so that when it shows a matching file you can see the rest of the tree leading to that file. –  Barmar Dec 21 '12 at 20:27
    
Makes sense, just wondering if there was a way to not show them. –  slm Dec 21 '12 at 20:35
    
If you're using -L 1, why not just do ls tstdir/*qm*? –  Barmar Dec 21 '12 at 20:37
    
If you're not relying on the format of the output, find tstdir -maxdepth 1 -type d -name '*qm*' should work. If you don't want to see the parent directories, just the children on that level, find tstdir -maxdepth 1 -type d -name '*qm*' |sed -e 's#tstdir/##' is a quick and dirty way to handle it. :D –  Jeremy Sandell Dec 21 '12 at 21:14
    
In this case, if from bash, I think my answer is near to be the quicker way to do the job. Using bash resolver is very quick and running a perl script or find | sed implie a fork (or two) who would take lot more time and resources. –  F. Hauri Dec 22 '12 at 8:03
add comment

2 Answers 2

up vote 5 down vote accepted

Yes, it's a bug. From man page:

BUGS
   Tree  does not prune "empty" directories when the -P and -I options are
   used.  Tree prints directories as it comes to them, so  cannot  accumu‐
   late  information  on files and directories beneath the directory it is
   printing.

... at all, -d switch ask to not print files:

    -d     List directories only.

So if you WANT use this, you could:

tree tstdir -P '*qm*' -L 1 | grep -B1 -- '-- .*qm'
|-- id1
|   `-- aqm_P1800-id1.0200.bin
--
|-- id165
|   `-- aqm_P1800-id165.0200.bin
|-- id166
|   `-- aqm_P1800-id166.0200.bin
--
|-- id17
|   `-- aqm_P1800-id17.0200.bin
--
|-- id18
|   `-- aqm_P1800-id18.0200.bin
--
|-- id2
|   `-- aqm_P1800-id2.0200.bin

At all, if you use -L 1,

   -L level
          Max display depth of the directory tree.

you could better use (in bash) this syntax:

cd tstdir
echo */*qm*

or

printf "%s\n" */*qm*

and if only dir is needed:

printf "%s\n" */*qm* | sed 's|/.*$||' | uniq

At all, you could do this very quickly if pure bash:

declare -A array;for file in  */*qm* ;do array[${file%/*}]='';done;echo "${!array[@]}"

This could be explained:

cd tstdir
declare -A array          # Declare associative array, (named ``array'')
for file in  */*qm* ;do   # For each *qm* in a subdirectory from there
    array[${file%/*}]=''  # Set a entry in array named as directory, containing nothing
  done
echo "${!array[@]}"         # print each entrys in array.

... if there is no file matching pattern, result would display *. so for perfect the job, there left to do:

resultList=("${!array[@]}")
[ -d "$resultList" ] || unset $resultList

(This would be a lot quicker than

declare -A array
for file in  */*qm*; do
    [ "$file" == "*/*qm*" ] || array[${file%/*}]=''
  done
echo "${!array[@]}"

)

share|improve this answer
add comment

You could use my arbo command. Install with:

ln -s "$PWD"/arbo.py ~/bin/arbo

Now you can do:

find tstdir -maxdepth 1 -iname '*qm*' |arbo --color

The output looks something like this, with the same colors as ls:

git arbo
bedup
├─ __init__.py
├─ __main__.py
├─ platform/__init__.py
├─ termupdates.py
├─ test_bedup.py
└─ tracking.py
setup.py
tox.ini
share|improve this answer
    
I've added some output. ~+ was bash-specific. –  Gabriel Jan 17 '13 at 15:30
    
~+ is the same as "$PWD". The bash manpage gives you a few more expansions like that. –  Gabriel Jan 17 '13 at 15:33
    
Yes you need Python 3. No, I won't give you a screenshot, you're asking for too much hand-holding. –  Gabriel Jan 17 '13 at 15:39
    
I'm not asking for my benefit, it makes your answer stronger for others who may be considering trying your software out. Try not to make judgments of others! –  slm Jan 17 '13 at 15:47
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.