如何在不使用“find”或“ls”命令的情况下递归列出Bash中的子目录?

时间:2010-01-28 11:42:37

标签: bash

我知道您可以使用find命令完成这项简单的工作,但我的任务是不使用findls来完成工作。我怎么能这样做?

10 个答案:

答案 0 :(得分:30)

你可以只使用shell

#!/bin/bash
recurse() {
 for i in "$1"/*;do
    if [ -d "$i" ];then
        echo "dir: $i"
        recurse "$i"
    elif [ -f "$i" ]; then
        echo "file: $i"
    fi
 done
}

recurse /path

或者如果你有bash 4.0

#!/bin/bash
shopt -s globstar
for file in /path/**
do
    echo $file
done

答案 1 :(得分:11)

尝试使用

tree -d

答案 2 :(得分:5)

以下是一种可能的实施方式:

# my_ls -- recursively list given directory's contents and subdirectories
# $1=directory whose contents to list
# $2=indentation when listing
my_ls() {
  # save current directory then cd to "$1"
  pushd "$1" >/dev/null
  # for each non-hidden (i.e. not starting with .) file/directory...
  for file in * ; do
    # print file/direcotry name if it really exists...
    test -e "$file" && echo "$2$file"
    # if directory, go down and list directory contents too
    test -d "$file" && my_ls "$file" "$2  "
  done
  # restore directory
  popd >/dev/null
}

# recursively list files in current
#  directory and subdirectories
my_ls .

作为练习,您可以考虑如何修改上述脚本以打印文件的完整路径(而不仅仅是缩进的文件/目录),可能会删除pushd / popd(和在过程中需要第二个参数$2

顺便提一下,请注意test XYZ && command的使用完全等同于if test XYZ ; then command ; fi(即command成功时执行test XYZ)。另请注意,test XYZ相当于[ XYZ ],即上述内容也相当于if [ XYZ ] ; then command ; fi。另请注意,任何分号;都可以替换为换行符,它们是等效的。

删除test -e "$file" &&条件(仅保留echo),看看会发生什么。

删除"$file"周围的双引号,看看当你列出的内容包含其中包含空格的文件名时会发生什么。在脚本顶部添加set -x(或者将其调用为sh -x scriptname.sh)以打开调试输出并查看详细信息(将调试输出重定向到文件,运行sh -x scriptname.sh 2>debugoutput.txt )。

还要列出隐藏文件(例如.bashrc):

...
for file in * .?* ; do
  if [ "$file" != ".." ] ; then
    test -e ...
    test -d ...
  fi
done
...

请注意使用!=(字符串比较)代替-ne(数字比较。)

另一种技巧是生成子壳而不是使用pushd / popd

my_ls() {
  # everything in between roundbrackets runs in a separatly spawned sub-shell
  (
    # change directory in sub-shell; does not affect parent shell's cwd
    cd "$1"
    for file in ...
      ...
    done
  )
}

请注意,在某些shell实现中,字符数有一个硬限制(~4k),可以作为参数传递给for(或者任何内置命令或外部命令)。由于shell expands, inline, * to a list of all matching filenames在实际执行for之前,如果*在包含大量文件的目录中展开,则会遇到麻烦(运行时会遇到同样的麻烦) ,在同一目录中说ls *,例如得到Command too long之类的错误。)

答案 3 :(得分:4)

因为它是针对bash的,所以还没有说过这是一个惊喜:
(globstar有效,来自bash 4.0 +)

shopt -s globstar nullglob dotglob
echo **/*/

就是这样 尾部斜杠/仅用于选择dirs。

选项globstar激活**(递归搜索)。 选项nullglob在没有文件/目录匹配时删除*。 选项dotglob包括以点(隐藏文件)开头的文件。

答案 4 :(得分:1)

du命令将递归列出子目录。

我不确定目录是否会被提及,但

答案 5 :(得分:1)

与Mark Byers一样,您可以使用echo *获取当前目录中所有文件的列表。

test[]命令/ builtin可以选择测试文件是否是目录。

申请递归,你就完成了。

答案 6 :(得分:0)

$ function f { for i in $1/*; do if [ -d $i ]; then echo $i; f $i; fi; done }
$ mkdir -p 1/2/3 2/3 3
$ f .
./1
./1/2
./1/2/3
./2
./2/3
./3

答案 7 :(得分:0)

从技术上讲,find | lsfind2perl直接使用perlFile::Find

$ find2perl -type d | perl
$ perl -MFile::Find -e'find(sub{-d&&print"$File::Find::name\n"},".")'

答案 8 :(得分:0)

基于this answer;使用shell options来实现所需的通行行为:

  • 启用**globstar(Bash 4.0或更高版本)
  • 使用dotglob
  • 包含隐藏目录
  • 如果与**/*/不匹配,则扩展为空字符串而不是nullglob

,然后使用printf with the %q formatting directive引用其中带有特殊字符的目录名称:

shopt -s globstar dotglob nullglob
printf '%q\n' **/*/

因此,如果您有has space之类的目录,甚至包含换行符,则将得到类似的输出

$ printf '%q\n' **/*/
$'has\nnewline/'
has\ space/

每行一个目录。

答案 9 :(得分:0)

我写了另一个迭代而不是递归的解决方案。

iterativePrintDir() {    
    dirs -c;
    currentd=$1
    if [ ! -d $currentd ];then
            echo "Diretorio não encontrado. \"$currentd\""
    fi
    pushd $(readlink -f $currentd)

    while popd 2>&-; do
            echo $currentd
            for d in $(dir $currentd); do
                    test -d "${currentd}/${d}" && pushd  "${currentd}/${d}"
            done
            currentd=$(dirs -l +0)
    done
}