MacLemon

Unixy on the fruity side

Save a Tree(1)

Estimated reading time: 3:30

Where are my files?

Do you know what’s on your storage devices? Well, of course you know, everything is there. Your whole life these days is stored on harddrives and USB thumbdrives or some kind of server. But what exactly is everything when your significant other or a user asks you if that precious file that supposedly was on the harddrive that just died actually was really there in the first place? This is where a tree comes in handy.

tree(1)

Meet tree(1) a handy utility that lists contents of directories in a tree-like format. For example the downloads directory of this site looks like this when listed by tree(1).

tree output of downloads/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ tree
.
├── J.mp.textexpander.zip
├── MacLemon.vcf
├── Unicode+Snippets.textexpander.zip
├── mlbackup-142-3.0.6.tar.gz
├── mlbackup-157-3.0.7.tar.gz
├── mlbackup-160-3.0.8.tar.gz
├── mlbackup-195-3.0.9.tar.gz
├── pepi.zawodsky.gpgkey
├── pepi.zawodsky.gpgkey.zip
└── tree.html

0 directories, 10 files

You could easily get the same level of insight with ls you rightfully think now. Unless, you start digging into deeper directory structures. For example your /Library/Application Support directory.

abbreviated tree output of Library
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
$ tree /Library/Application\ Support | less
/Library/Application\ Support
├── Aperture
│   └── Plug-Ins
│       ├── Edit
│       │   └── BorderFXEditPlugin.ApertureEdit
│       │       └── Contents
│       │           ├── Frameworks
│       │           │   └── IFImageView.framework
│       │           │       ├── IFImageView -> Versions/Current/IFImageView
│       │           │       ├── PrivateHeaders -> Versions/Current/PrivateHeaders
│       │           │       ├── Resources -> Versions/Current/Resources
│       │           │       └── Versions
│       │           │           ├── A
│       │           │           │   ├── IFImageView
│       │           │           │   ├── PrivateHeaders
│       │           │           │   │   ├── CGImageView.h
│       │           │           │   │   ├── IFImageBrowser.h
│       │           │           │   │   ├── IFImageObject.h
│       │           │           │   │   ├── IFImageView.h
│       │           │           │   │   ├── IFLayer.h
│       │           │           │   │   ├── IFLayerController.h
│       │           │           │   │   ├── IFLayerImage.h
│       │           │           │   │   ├── IFLayerImageMask.h
│       │           │           │   │   ├── IFLayerStack.h
│       │           │           │   │   ├── IFLayerTiltShift.h
│       │           │           │   │   ├── IFPreviewCanvas.h
│       │           │           │   │   ├── IFSelectionGuides.h
│       │           │           │   │   ├── IFSplitView.h
│       │           │           │   │   ├── MUPhotoView.h
│       │           │           │   │   ├── SelectionDefocus.h
│       │           │           │   │   ├── SelectionMarker.h
│       │           │           │   │   ├── SelectionMarkerBorder.h
│       │           │           │   │   ├── SelectionMarkerImage.h
│       │           │           │   │   └── SelectionTiltShift.h
│       │           │           │   └── Resources
│       │           │           │       ├── Info.plist
│       │           │           │       ├── ReferenceImage.tiff
│       │           │           │       ├── hud-edit_cancel-N.tiff
│       │           │           │       ├── hud-edit_cancel-P.tiff
│       │           │           │       ├── zoom_larger.tif
│       │           │           │       └── zoom_smaller.tif
│       │           │           └── Current -> A
│       │           ├── Info.plist
│       │           ├── MacOS
│       │           │   └── BorderFXEditPlugin
│       │           ├── PlugIns
│       │           │   └── SoftRectangleImageUnit.plugin
│       │           │       └── Contents
│       │           │           ├── Info.plist
│       │           │           ├── MacOS
│       │           │           │   └── SoftRectangleImageUnit
│       │           │           └── Resources
│       │           │               ├── Description.plist
│       │           │               ├── English.lproj
│       │           │               │   ├── Description.strings
│       │           │               │   └── InfoPlist.strings
│       │           │               └── SoftRectangle.cikernel

Now you get the idea why this can be quite helpful in determining the contents of a folder.

How about a nice list of your Podcasts in iTunes? Here we make use of the -N flag to output diacritic characters like the german umlauts (äöüÄÖÜ), instead of octal escaping them. The OS X Terminal as well as iTerm are well UTF-8 capable.

tree output of iTunes Podcasts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ tree -N ~/Music/iTunes/iTunes\ Media/Podcasts
/Users/pepi/Music/iTunes/iTunes Media/Podcasts
├── Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_
│   ├── 01 CHAT001 Woher kommen eigentlich diese Cocoaheads und was machen die so_ 1.m4a
│   ├── 02 CHAT002 AirPort Express, Xcode 4.0.2, Git Achievements, Pow.m4a
│   ├── 03 CHAT003 iOS 4.3.3 & Jailbreak, IRC, CodingKitchen Wien, BarCamp Graz, Mobile CouchBase, SuperPin, What License_, Git CheatSheet 1.m4a
│   ├── 27 CHW027_ D-Scrum_ Modernes IT-Projektmanagement auch für Freelancer und KMUs 1.m4v
│   └── CHAT000 Hallo Welt 1.m4a
├── Hot Cocoa
│   ├── Hot Cocoa 3_ The Coin Flipper.m4v
│   ├── Hot Cocoa 4_ CoreData - Part I.mp4
│   ├── Hot Cocoa 5_ Core Data - Part II.mp4
│   └── Hot Cocoa 6_ Intro to iPhone Programming.mp4
├── Learn By The Drop Videos
│   └── Creating An RSS Feed Using Views.m4v
├── Mac Power Users
│   ├── Screencast_ OmniFocus Ninja Tricks (Part 2 of 3).m4v
│   └── Screencast_ OmniFocus Ninja Tricks (Part 3 of 3).mov
├── MacBreak Dev
│   ├── Checking Applications with Instruments.mov
│   ├── Data Structures within Core Data.mov
│   ├── How Properties and Core Data work together.mov
│   └── What is DTrace_.mov
├── Unknown Podcast
│   ├── 9) Cathy Shive on Design.mp3
│   ├── Chatting with Sofa.m4a
│   ├── Indie Support with Kirby Turner.mp3
│   ├── Jiva Devoe.mp3
│   └── NSBrief_11_Appsterdam.m4a
└── iOS Programmierung (iPad, iPhone, Xcode, objective-c)
    ├── Die ToDoList (Podcast 004).mp4
    ├── Macoun 2012 UICollectionView (Podcast 036).mp4
    └── iOS-Hybrid bzw. iOS-UniversalApp (Podcast 003).mp4

7 directories, 24 files

A trip down the memory lane for those who have seen the old BBS days with an Acoustic coupler or analog modem. Back in those days, and even in the early days of the internet many FTP servers had an index file for you to download locally. Search functions did not exists and connection speeds were slow, so you just downloaded a full file tree and searched through that one locally. Such a listing can be created with tree(1) as well.

tree output of Podcasts in FTP archive style
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ tree -Nifh ~/Music/iTunes/iTunes\ Media/Podcasts
/Users/pepi/Music/iTunes/iTunes Media/Podcasts
[ 238]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_
[2.0M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_/01 CHAT001 Woher kommen eigentlich diese Cocoaheads und was machen die so_ 1.m4a
[1.4M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_/02 CHAT002 AirPort Express, Xcode 4.0.2, Git Achievements, Pow.m4a
[2.1M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_/03 CHAT003 iOS 4.3.3 & Jailbreak, IRC, CodingKitchen Wien, BarCamp Graz, Mobile CouchBase, SuperPin, What License_, Git CheatSheet 1.m4a
[457M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_/27 CHW027_ D-Scrum_ Modernes IT-Projektmanagement auch für Freelancer und KMUs 1.m4v
[ 75K]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Cocoaheads Austria - Die freundlichste Entwickler Gemeinschaft für Mac OS X und iOS_/CHAT000 Hallo Welt 1.m4a
[ 204]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Hot Cocoa
[ 98M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Hot Cocoa/Hot Cocoa 3_ The Coin Flipper.m4v
[ 25M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Hot Cocoa/Hot Cocoa 4_ CoreData - Part I.mp4
[ 20M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Hot Cocoa/Hot Cocoa 5_ Core Data - Part II.mp4
[ 32M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Hot Cocoa/Hot Cocoa 6_ Intro to iPhone Programming.mp4
[ 102]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Learn By The Drop Videos
[ 19M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Learn By The Drop Videos/Creating An RSS Feed Using Views.m4v
[ 136]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Mac Power Users
[146M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Mac Power Users/Screencast_ OmniFocus Ninja Tricks (Part 2 of 3).m4v
[158M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Mac Power Users/Screencast_ OmniFocus Ninja Tricks (Part 3 of 3).mov
[ 204]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/MacBreak Dev
[ 39M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/MacBreak Dev/Checking Applications with Instruments.mov
[ 41M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/MacBreak Dev/Data Structures within Core Data.mov
[ 64M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/MacBreak Dev/How Properties and Core Data work together.mov
[ 57M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/MacBreak Dev/What is DTrace_.mov
[ 238]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast
[ 18M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast/9) Cathy Shive on Design.mp3
[ 27M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast/Chatting with Sofa.m4a
[ 47M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast/Indie Support with Kirby Turner.mp3
[ 49M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast/Jiva Devoe.mp3
[ 50M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/Unknown Podcast/NSBrief_11_Appsterdam.m4a
[ 170]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/iOS Programmierung (iPad, iPhone, Xcode, objective-c)
[153M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/iOS Programmierung (iPad, iPhone, Xcode, objective-c)/Die ToDoList (Podcast 004).mp4
[262M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/iOS Programmierung (iPad, iPhone, Xcode, objective-c)/Macoun 2012 UICollectionView (Podcast 036).mp4
[ 97M]  /Users/pepi/Music/iTunes/iTunes Media/Podcasts/iOS Programmierung (iPad, iPhone, Xcode, objective-c)/iOS-Hybrid bzw. iOS-UniversalApp (Podcast 003).mp4

7 directories, 24 files

I’ve included -h in this example to also give me a human readable size information in addition to the full (unescaped, unquoted) path to each file. There are flags to adapt the output to your liking.

With a rather large fileserver, like for a small office, that tree file can grow substantially. My tests have shown that a (probably non-typical) 1TB office server sharepoint with 369910 directories and 1285341 files took up 346MB which boiled down to just 17MB when compressed with gzip(1). So this is quite well suited for archiving over time.

The last example I want to show you is a nice file listing for a webserver. So here is this server’s download directory index as somewhat nice HTML created with tree(1).

tree output of downloads on this server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <meta name="Author" content="Made by 'tree'">
 <meta name="GENERATOR" content="$Version: $ tree v1.6.0 (c) 1996 - 2011 by Steve Baker, Thomas Moore, Francesc Rocher, Kyosuke Tokoro $">
 <title>MacLemon downloads</title>
 <style type="text/css">
  <!--
  BODY { font-family : ariel, monospace, sans-serif; }
  P { font-weight: normal; font-family : ariel, monospace, sans-serif; color: black; background-color: transparent;}
  B { font-weight: normal; color: black; background-color: transparent;}
  A:visited { font-weight : normal; text-decoration : none; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:link    { font-weight : normal; text-decoration : none; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:hover   { color : #000000; font-weight : normal; text-decoration : underline; background-color : yellow; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  A:active  { color : #000000; font-weight: normal; background-color : transparent; margin : 0px 0px 0px 0px; padding : 0px 0px 0px 0px; display: inline; }
  .VERSION { font-size: small; font-family : arial, sans-serif; }
  .NORM  { color: black;  background-color: transparent;}
  .FIFO  { color: purple; background-color: transparent;}
  .CHAR  { color: yellow; background-color: transparent;}
  .DIR   { color: blue;   background-color: transparent;}
  .BLOCK { color: yellow; background-color: transparent;}
  .LINK  { color: aqua;   background-color: transparent;}
  .SOCK  { color: fuchsia;background-color: transparent;}
  .EXEC  { color: green;  background-color: transparent;}
  -->
 </style>
</head>
<body>
  <h1>MacLemon downloads</h1><p>
  <a href="https://maclemon.at/downloads">https://maclemon.at/downloads</a><br>
  ├── [1.4K&nbsp;Jan&nbsp;17&nbsp;16:32]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/J.mp.textexpander.zip">J.mp.textexpander.zip</a><br>
  ├── [&nbsp;29K&nbsp;Jan&nbsp;17&nbsp;15:52]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/MacLemon.vcf">MacLemon.vcf</a><br>
  ├── [2.8M&nbsp;Jan&nbsp;17&nbsp;16:34]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/Unicode%2BSnippets.textexpander.zip">Unicode+Snippets.textexpander.zip</a><br>
  ├── [1.8M&nbsp;Feb&nbsp;21&nbsp;&nbsp;2011]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/mlbackup-142-3.0.6.tar.gz">mlbackup-142-3.0.6.tar.gz</a><br>
  ├── [2.9M&nbsp;Mar&nbsp;&nbsp;7&nbsp;&nbsp;2011]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/mlbackup-157-3.0.7.tar.gz">mlbackup-157-3.0.7.tar.gz</a><br>
  ├── [3.5M&nbsp;Mar&nbsp;28&nbsp;&nbsp;2011]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/mlbackup-160-3.0.8.tar.gz">mlbackup-160-3.0.8.tar.gz</a><br>
  ├── [3.5M&nbsp;Mar&nbsp;22&nbsp;&nbsp;2012]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/mlbackup-195-3.0.9.tar.gz">mlbackup-195-3.0.9.tar.gz</a><br>
  ├── [1.7K&nbsp;Sep&nbsp;24&nbsp;&nbsp;2009]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/pepi.zawodsky.gpgkey">pepi.zawodsky.gpgkey</a><br>
  ├── [1.9K&nbsp;Jan&nbsp;17&nbsp;15:35]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/pepi.zawodsky.gpgkey.zip">pepi.zawodsky.gpgkey.zip</a><br>
  └── [&nbsp;&nbsp;&nbsp;0&nbsp;Mar&nbsp;18&nbsp;&nbsp;2:36]&nbsp;&nbsp;<a href="https://maclemon.at/downloads/tree.html">tree.html</a><br>
  <br><br>
  </p>
  <p>

0 directories, 10 files
  <br><br>
  </p>
  <hr>
  <p class="VERSION">
       tree v1.6.0 © 1996 - 2011 by Steve Baker and Thomas Moore <br>
       HTML output hacked and copyleft © 1998 by Francesc Rocher <br>
       Charsets / OS/2 support © 2001 by Kyosuke Tokoro
  </p>
</body>
</html>

You can see what the output looks like on the web at /downloads/tree.html. Certainly beats the standard Apache directory listing aesthetics and it can be further customized by adding some CSS.

For a full list of customisation options for output see man 1 tree as usual.

tree is not a standard command that comes included with OS X but is available from most popular package managers like port(1) or build your own from source.

Please don’t print this blog post, save a tree!