ChatGPT解决这个技术问题 Extra ChatGPT

Linux command to print directory structure in the form of a tree

Is there any linux command that I can call from a Bash script that will print the directory structure in the form of a tree, e.g.,

folder1
   a.txt
   b.txt
folder2
   folder3
Just run find. Or find . -not -path '*/\.*' to hide files and folders starting with .. If you want to have output with spaces, as in the question, use it with this "find prettifier" script: find . -not -path '*/\.*' | python -c "import sys as s;s.a=[];[setattr(s,'a',list(filter(lambda p: c.startswith(p+'/'),s.a)))or (s.stdout.write(' '*len(s.a)+c[len(s.a[-1])+1 if s.a else 0:])or True) and s.a.append(c[:-1]) for c in s.stdin]"
Shouldn't such questions get migrated to SuperUser rather than closed ?
i dont think this question deserves to be closed as "off topic". The tags seem to be right.
The policy of closing questions without migrating is harmful to both stackoverflow and human knowledge in general. In the last 3 days, every single questions I googled and came across was closed for similar reasoning, and no more activity was able to happen. This means no one can update it, no one can give a better answer, and it makes stackoverflow look shortsighted or elitist. Stackoverflow should consider requiring a migration when a topic is found to have these conditions.
I agree with @NickYeates I am here in late September of 2017 still finding answers to this same question. Think long term when we design these question and answer policies!

D
Dathan

Is this what you're looking for tree? It should be in most distributions (maybe as an optional install).

~> tree -d /proc/self/
/proc/self/
|-- attr
|-- cwd -> /proc
|-- fd
|   `-- 3 -> /proc/15589/fd
|-- fdinfo
|-- net
|   |-- dev_snmp6
|   |-- netfilter
|   |-- rpc
|   |   |-- auth.rpcsec.context
|   |   |-- auth.rpcsec.init
|   |   |-- auth.unix.gid
|   |   |-- auth.unix.ip
|   |   |-- nfs4.idtoname
|   |   |-- nfs4.nametoid
|   |   |-- nfsd.export
|   |   `-- nfsd.fh
|   `-- stat
|-- root -> /
`-- task
    `-- 15589
        |-- attr
        |-- cwd -> /proc
        |-- fd
        | `-- 3 -> /proc/15589/task/15589/fd
        |-- fdinfo
        `-- root -> /

27 directories

sample taken from maintainer's web page.

You can add the option -L # where # is replaced by a number, to specify the max recursion depth.

Remove -d to display also files.


Note for any visitor seeing this: remove -d to display files also!
Note for any visitor seeing this: The man page lists a truckload of more flags for you :)
To install on Mac OS X w/Homebrew: brew install tree
To install on cygwin apt-cyg install tree (assuming you've installed apt-cyg)
Not even Ubuntu 16.04 comes with this. Use apt-get install tree will install it.
D
Dem Pilafian

You can use this one:

ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/   /' -e 's/-/|/'

It will show a graphical representation of the current sub-directories without files in a few seconds, e.g. in /var/cache/:

   .
   |-apache2
   |---mod_cache_disk
   |-apparmor
   |-apt
   |---archives
   |-----partial
   |-apt-xapian-index
   |---index.1
   |-dbconfig-common
   |---backups
   |-debconf

Source


If you want it with spaces, more like the OP requested, then this: ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\// /g' -e 's/^/ /'
with files: find . | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
@Ben thanks for that, all other (non-tree) answers produce a result that does not look perfectly correct to me. For example in this answer, there should be a line going down from apt then horizontally to archives, instead it comes down from . and goes to archives, only because of more indentation you can guess that it's actually a subfolder of apt. So you could as well just leave the lines away, it's at least not misleading then.
@JavaSheriff yours is great
Really neat. Great things can be done with simple tools ... when you know what you're doing!
P
Pavan Kumar

This command works to display both folders and files.

find . | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"

Example output:

.
 |-trace.pcap
 |-parent
 | |-chdir1
 | | |-file1.txt
 | |-chdir2
 | | |-file2.txt
 | | |-file3.sh
 |-tmp
 | |-json-c-0.11-4.el7_0.x86_64.rpm

Source: Comment from @javasheriff here. Its submerged as a comment and posting it as answer helps users spot it easily.


for python3 I found find . |grep -vE 'pyc|swp|__init' | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/" working well
This was a great help as we didn't have tree installed on a certain server. This will most likely work on any standard Linux system.
J
JavaSheriff

Since it was a successful comment, I am adding it as an answer: To print the directory structure in the form of a tree, WITH FILES

 find . | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/" 

If you would like to sort your result list then combine the nice solution above with sort this way: find . | sort | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
J
John Deters

To add Hassou's solution to your .bashrc, try:

alias lst='ls -R | grep ":$" | sed -e '"'"'s/:$//'"'"' -e '"'"'s/[^-][^\/]*\//--/g'"'"' -e '"'"'s/^/   /'"'"' -e '"'"'s/-/|/'"'"

Beware of the newline character at the end of the first line if copying this directly
Nice alias. But there is missing ' ' (2 single quote chars) at the end. It works even without it, but... if you want to add some more commands at the end you will see the literal is not complete. So it should go alias lst='ls -R | grep ":$" | sed -e '"'"'s/:$//'"'"' -e '"'"'s/[^-][^\/]*\//--/g'"'"' -e '"'"'s/^/ /'"'"' -e '"'"'s/-/|/'"'"''
m
msa

Since I was not too happy with the output of other (non-tree) answers (see my comment at Hassou's answer), I tried to mimic trees output a bit more.

It's similar to the answer of Robert but the horizontal lines do not all start at the beginning, but where there are supposed to start. Had to use perl though, but in my case, on the system where I don't have tree, perl is available.

ls -aR | grep ":$" | perl -pe 's/:$//;s/[^-][^\/]*\//    /g;s/^    (\S)/└── \1/;s/(^    |    (?= ))/│   /g;s/    (\S)/└── \1/'

Output (shortened):

.
└── fd
└── net
│   └── dev_snmp6
│   └── nfsfs
│   └── rpc
│   │   └── auth.unix.ip
│   └── stat
│   └── vlan
└── ns
└── task
│   └── 1310
│   │   └── net
│   │   │   └── dev_snmp6
│   │   │   └── rpc
│   │   │   │   └── auth.unix.gid
│   │   │   │   └── auth.unix.ip
│   │   │   └── stat
│   │   │   └── vlan
│   │   └── ns

Suggestions to avoid the superfluous vertical lines are welcome :-)

I still like Ben's solution in the comment of Hassou's answer very much, without the (not perfectly correct) lines it's much cleaner. For my use case I additionally removed the global indentation and added the option to also ls hidden files, like so:

ls -aR | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//  /g'

Output (shortened even more):

.
  fd
  net
    dev_snmp6
    nfsfs
    rpc
      auth.unix.ip
    stat
    vlan
  ns

The only way to get rid of the unwanted vertical lines when processing the output of ls -R is to proceed from last to first line. See the awk-based solution I provided, which you could easily adapt to perl.
R
Robert

I'm prettifying the output of @Hassou's answer with:

ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//──/g' -e 's/─/├/' -e '$s/├/└/'

This is much like the output of tree now:

.
├─pkcs11
├─pki
├───ca-trust
├─────extracted
├───────java
├───────openssl
├───────pem
├─────source
├───────anchors
├─profile.d
└─ssh

You can also make an alias of it:

alias ltree=$'ls -R | grep ":$" | sed -e \'s/:$//\' -e \'s/[^-][^\/]*\//──/g\' -e \'s/─/├/\' -e \'$s/├/└/\''

BTW, tree is not available in some environment, like MinGW. So the alternate is helpful.


gitbash on windows does not like the last expression, it says that it is not terminated
@LeosLiterak: On Windows, native tree should be available with Command Prompt (cmd) or PowerShell though :-)
R
Raj kamal

Adding the below function in bashrc lets you run the command without any arguments which displays the current directory structure and when run with any path as argument, will display the directory structure of that path. This avoids the need to switch to a particular directory before running the command.

function tree() {
    find ${1:-.} | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/"
}

This works in gitbash too.

Source: Comment from @javasheriff here


y
ylspirit

You can also use the combination of find and awk commands to print the directory tree. For details, please refer to "How to print a multilevel tree directory structure using the linux find and awk combined commands"

find . -type d | awk -F'/' '{ 
depth=3;
offset=2;
str="|  ";
path="";
if(NF >= 2 && NF < depth + offset) {
    while(offset < NF) {
        path = path "|  ";
        offset ++;
    }
    print path "|-- "$NF;
}}'

F
François Tonneau

The best answer is, of course, tree. But, to improve on other answers that rely on grepping the output of ls -R, here is a shell script that uses awk to print a tree of subdirectories. First, an example of output:

.
└── matching
    ├── bib
    ├── data
    │   └── source
    │       └── html
    ├── data
    │   └── plots
    ├── method
    │   ├── info
    │   └── soft
    │       ├── imgs
    │       │   ├── ascii
    │       │   └── symbol
    │       └── js
    └── ms

Then, the code:

ls -qLR 2>/dev/null \
| grep '^./' \
| sed -e 's,:$,,' \
| awk '
    function tip(new) { stem = substr(stem, 1, length(stem) - 4) new }
    {
        path[NR] = $0
    }
    END {
        elbow = "└── "; pipe = "│   "; tee = "├── "; blank = "    "
        none = ""
        #
        # Model each stem on the previous one, going bottom up.
        for (row = NR; row > 0; row--) {
            #
            # gsub: count (and clean) all slash-ending components; hence,
            # reduce path to its last component.
            growth = gsub(/[^/]+\//, "", path[row]) - slashes
            if (growth == 0) {
                tip(tee)
            }
            else if (growth > 0) {
                if (stem) tip(pipe) # if...: stem is empty at first!
                for (d = 1; d < growth; d++) stem = stem blank
                stem = stem elbow
            }
            else {
                tip(none)
                below = substr(stem, length(stem) - 4, 4)
                if (below == blank) tip(elbow); else tip(tee)
            }
            path[row] = stem path[row]
            slashes += growth
        }
        root = "."; print root
        for (row = 1; row <= NR; row++) print path[row]
    }
'

The code gives better-looking results than other solutions because in a tree of subdirectories, the decorations in any branch depend on the branches below it. Hence, we need to process the output of ls -R in reverse order, from the last line to the first.

A shell function based on this code (with a few options) can be found here:

https://github.com/ftonneau/t.sh