以树形结构打印目录结构的Linux命令

691

有没有一个Linux命令可以从Bash脚本中调用,以树形结构的形式打印目录结构,例如:

folder1
   a.txt
   b.txt
folder2
   folder3

16
只需运行 find 命令。或者使用 find . -not -path '*/\.*' 来隐藏以 . 开头的文件和文件夹。如果您希望输出结果中包含空格(如问题所示),请结合这个“find prettifier”脚本使用: 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]" - user
14
这些问题不应该关闭,而是移至SuperUser吗? - Balmipour
15
我认为这个问题不应该被关闭为“离题”。标签看起来是正确的。 - Sanket Berde
26
不迁移而关闭问题的政策对stackoverflow和人类知识都是有害的。在过去3天里,我谷歌搜索并遇到的每一个问题都因类似的原因被关闭,没有更多的活动可以发生。这意味着没有人能够更新它,也没有人能够提供更好的答案,这让stackoverflow看起来很短视或精英主义。当发现一个主题具有这些条件时,Stackoverflow应考虑要求迁移。 - Nay
6
我同意@NickYeates的看法。到2017年9月底,我还在寻找这个相同问题的答案。我们设计这些问题和答案政策时应考虑长远! - Alex
显示剩余2条评论
11个回答

988

这是你在寻找的吗 tree?它应该在大多数发行版中(可能作为可选安装)。

~> 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

这是从维护者的网页中获取的例子。

你可以添加选项-L #,其中#被替换为数字,以指定最大递归深度。

删除-d以显示文件。


78
注意:对于任何看到这句话的访问者,请删除“-d”以显示文件! - q9f
38
注意:任何查看此信息的访客,请注意手册中列出了更多的标志。 :) - oivvio
62
在Mac OS X上使用Homebrew安装:brew install tree - funfuntime
2
在Cygwin上安装 apt-cyg install tree(假设您已经安装了apt-cyg)。 - blockloop
7
甚至Ubuntu 16.04也没有自带这个功能。使用apt-get install tree可以安装它。 - Romeo Sierra
显示剩余13条评论

449

您可以使用这个:

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

它将在几秒钟内显示当前子目录中没有文件的图形表示,例如在/var/cache/中:

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

来源


13
如果你需要带有空格的格式,更像原始问题(OP)中的要求,那么可以使用以下命令:ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\// /g' -e 's/^/ /'请注意,本翻译只涉及对原文内容的转换,不包括任何解释或其他附加信息。 - Ben
111
使用以下命令对文件进行格式化输出:find . | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/" - JavaSheriff
3
@Ben,谢谢您的回答。除了“tree”之外的所有答案都对我来说看起来不完全正确。例如,在这个答案中,应该有一条从“apt”向下延伸然后水平延伸到“archives”的线,但实际上它是从“.”向下延伸并到达“archives”,只有因为更多的缩进,您才能猜出它实际上是“apt”的子文件夹。因此,您也可以直接省略这些线,至少这样就不会引起误导了。 - msa
1
@JavaSheriff,你的真棒。 - lefft
1
非常整洁。当你知道你在做什么时,简单的工具能够实现伟大的事情! - CrimsonDark
显示剩余6条评论

82

这个命令可以显示文件夹和文件

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

示例输出:

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

来源:@javasheriff在这里的评论中提到。它作为评论被隐藏了,将其发布为答案可以帮助用户更容易地找到它。


1
对于Python3,我发现find。 | grep -vE'pyc | swp | __init' | sed -e“s / [^-] [^ \ /] * \ // | / g”-e“s / | \([^ ])/ | - \ 1 /”能够很好地工作。 - patroqueeet
2
这真是太有帮助了,因为我们在某个服务器上没有安装树形结构。这很可能适用于任何标准的Linux系统。 - jaw
谢谢。我们如何使结果按字母顺序排列? - laviex
@lavlex 你可以使用 find * | sed ... 命令来排序顶级目录,但这是我目前愿意思考的范围之内了。 - FlipMcF

44

既然这是一条成功的评论,我将其添加为答案:
以树形结构打印目录结构,
包括文件

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

5
如果您想对结果列表进行排序,则可以将上面的好解决方案与 sort 结合使用,方法如下: find . | sort | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/" - zappee

22

由于我对其他(非tree)答案的输出不是太满意(请参见我的评论:Hassou's answer),因此我试图更加模仿tree的输出。

它类似于Robert的答案,但是水平线不都从开头开始,而是从应该开始的地方开始。不过我必须使用perl,但在我没有tree的系统上,perl是可用的。

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

输出(简化版):

.
└── 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

欢迎提出避免多余的垂直线的建议 :-)
我仍然非常喜欢Hassou答案评论中Ben的解决方案,没有(不完全正确的)线条会更加清晰。对于我的用例,我另外去掉了全局缩进,并添加了选项以便ls隐藏文件,如下所示:
ls -aR | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//  /g'

输出(更加简洁):

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

处理 ls -R 命令输出中不想要的垂直线的唯一方法是从最后一行开始进行处理。请参考我提供的基于 awk 的解决方案,您可以轻松地将其适应到 perl 中。 - François Tonneau

21

要将Hassou的解决方案添加到您的.bashrc中,请尝试:

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

6
如果直接复制,请注意第一行末尾的换行符。 - Rahul
3
别名很好,但是末尾缺少两个单引号字符。没有这个也可以工作,但是如果你想在末尾添加一些更多的命令,你会发现字面上不完整。所以应该这样写: alias lst='ls -R | grep ":$" | sed -e '"'"'s/:$//'"'"' -e '"'"'s/[^-][^\/]*\//--/g'"'"' -e '"'"'s/^/ /'"'"' -e '"'"'s/-/|/'"'"'' - Hero Qu

9

最佳答案当然是tree。但是,为了改进那些依赖于对ls -R输出进行grep的其他答案,这里提供了一个使用awk打印子目录树的shell脚本。首先,一个输出示例:

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

然后,代码:

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]
    }
'

这段代码比其他解决方案的结果看起来更美观,因为在子目录树中,任何分支的装饰都取决于它下面的分支。因此,我们需要倒序处理ls -R的输出,从最后一行到第一行。


你在哪个系统上测试它的?我在 Mac 上的 gsub 行收到了一个错误 awk: nonterminated character class [^ - Andrei
@Andrei 我在 Arch 上测试了它,其中 awk 是符号链接到 gawk (= GNU awk)。所以这可能是另一个讨厌的 awk/gawk 差异。也许你应该在字符类内部转义斜杠:/[^/]+// - François Tonneau

8

我正在对@Hassou的答案输出进行美化,使用如下代码:

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

这很像现在tree的输出结果:
.
├─pkcs11
├─pki
├───ca-trust
├─────extracted
├───────java
├───────openssl
├───────pem
├─────source
├───────anchors
├─profile.d
└─ssh

你可以创建它的别名:
alias ltree=$'ls -R | grep ":$" | sed -e \'s/:$//\' -e \'s/[^-][^\/]*\//──/g\' -e \'s/─/├/\' -e \'$s/├/└/\''

顺便提一下,像MinGW这样的环境中无法使用tree命令。因此替代方案是有帮助的。


在Windows上使用Git Bash时,它不喜欢最后一个表达式,它会说它没有终止。 - Leos Literak
2
@LeosLiterak:在Windows上,本地的 tree 命令应该可以在命令提示符 (cmd) 或 PowerShell 中使用 :-) - msa

5
在bashrc中添加以下函数,可以使命令无需任何参数即可显示当前目录结构,而带有路径参数时将显示该路径的目录结构。这样就避免了在运行命令之前需要切换到特定目录的需要。
function tree() {
    find ${1:-.} | sed -e "s/[^-][^\/]*\//  |/g" -e "s/|\([^ ]\)/|-\1/"
}

这也适用于gitbash。
来源:@javasheriff的评论 在这里

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接