如何在不使用“find”或“ls”命令的情况下递归列出 Bash 中的子目录?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2154166/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to recursively list subdirectories in Bash without using "find" or "ls" commands?
提问by Raghubansh Mani
I know you can use the find
command for this simple job, but I got an assignment not to use find
or ls
and do the job. How can I do that?
我知道你可以使用这个find
命令来完成这个简单的工作,但我得到了一个不使用find
或ls
完成这项工作的任务。我怎样才能做到这一点?
回答by ghostdog74
you can do it with just the shell
你可以只用外壳来做
#!/bin/bash
recurse() {
for i in ""/*;do
if [ -d "$i" ];then
echo "dir: $i"
recurse "$i"
elif [ -f "$i" ]; then
echo "file: $i"
fi
done
}
recurse /path
OR if you have bash 4.0
或者如果你有 bash 4.0
#!/bin/bash
shopt -s globstar
for file in /path/**
do
echo $file
done
回答by Alberto Zaccagni
Try using
尝试使用
tree -d
回答by vladr
Below is one possible implementation:
下面是一种可能的实现:
# my_ls -- recursively list given directory's contents and subdirectories
# =directory whose contents to list
# =indentation when listing
my_ls() {
# save current directory then cd to ""
pushd "" >/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 "$file"
# if directory, go down and list directory contents too
test -d "$file" && my_ls "$file" " "
done
# restore directory
popd >/dev/null
}
# recursively list files in current
# directory and subdirectories
my_ls .
As an exercise you can think of how to modify the above script to print full paths to files (instead of just indented file/dirnames), possibly getting rid of pushd
/popd
(and of the need for the second parameter $2
) in the process.
作为练习,您可以考虑如何修改上述脚本以打印文件的完整路径(而不仅仅是缩进的文件/目录名),可能会在此过程中摆脱pushd
/ popd
(以及对第二个参数的需求$2
)。
Incidentally, note the use of test XYZ && command
which is fully equivalent to if test XYZ ; then command ; fi
(i.e. execute command
if test XYZ
is successful). Also note that test XYZ
is equivalent to [ XYZ ]
, i.e. the above is also equivalent to if [ XYZ ] ; then command ; fi
. Also note that any semicolon ;
can be replaced with a newline, they are equivalent.
顺便说一下,注意其中的使用test XYZ && command
完全等同于if test XYZ ; then command ; fi
(即command
如果test XYZ
成功则执行)。还要注意test XYZ
等价于[ XYZ ]
,即上面也等价于if [ XYZ ] ; then command ; fi
。另请注意,任何分号;
都可以用换行符替换,它们是等效的。
Remove the test -e "$file" &&
condition (only leave the echo
) and see what happens.
删除test -e "$file" &&
条件(只留下echo
),看看会发生什么。
Remove the double-quotes around "$file"
and see what happens when the directory whose contents you are listing contains filenames with spaces in them. Add set -x
at the top of the script (or invoke it as sh -x scriptname.sh
instead) to turn on debug output and see what's happenning in detail (to redirect debug output to a file, run sh -x scriptname.sh 2>debugoutput.txt
).
删除周围的双引号"$file"
,看看当您列出其内容的目录包含其中包含空格的文件名时会发生什么。set -x
在脚本顶部添加(或sh -x scriptname.sh
改为调用它)以打开调试输出并查看发生的详细情况(将调试输出重定向到文件,运行sh -x scriptname.sh 2>debugoutput.txt
)。
To also list hidden files (e.g. .bashrc
):
还要列出隐藏文件(例如.bashrc
):
...
for file in * .?* ; do
if [ "$file" != ".." ] ; then
test -e ...
test -d ...
fi
done
...
Note the use of !=
(string comparison) instead of -ne
(numeric comparison.)
请注意使用!=
(字符串比较)而不是-ne
(数字比较。)
Another technique would be to spawn subshells instead of using pushd
/popd
:
另一种技术是产生子壳而不是使用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 ""
for file in ...
...
done
)
}
Note that on some shell implementations there is a hard limit (~4k) on the number of characters which can be passed as an argument to for
(or to any builtin, or external command for that matter.) Since the shell expands, inline, *
to a list of all matching filenamesbefore actually performing for
on it, you can run into trouble if *
is expanded inside a directory with a lot of files (same trouble you'll run into when running, say ls *
in the same directory, e.g. get an error like Command too long
.)
请注意,在某些 shell 实现中,可以作为参数传递给for
(或任何内置命令或外部命令)的字符数有硬限制(~4k)。由于 shell扩展,内联,*
到在实际执行之前所有匹配文件名的列表,for
如果*
在包含大量文件的目录中展开,您可能会遇到问题(运行时会遇到同样的问题,比如ls *
在同一目录中,例如得到一个错误Command too long
.)
回答by vladr
Since it is for bash, it is a surprise that this hasn't been already said:
(globstar valid from bash 4.0+)
由于它是用于 bash 的,令人惊讶的是,这还没有被说出来:
(globstar 从 bash 4.0+ 开始有效)
shopt -s globstar nullglob dotglob
echo **/*/
That's all.
The trailing slash /
is there to select only dirs.
就这样。
尾部斜杠/
仅用于选择目录。
Option globstar
activates the **
(search recursivelly).
Option nullglob
removes an *
when it matches no file/dir.
Option dotglob
includes files that start wit a dot (hidden files).
选项globstar
激活**
(递归搜索)。选项在不匹配任何文件/目录时nullglob
删除*
。选项dotglob
包括以点开头的文件(隐藏文件)。
回答by schot
Like Mark Byers said you can use echo *
to get a list of all files in the current directory.
就像 Mark Byers 所说,您可以使用echo *
来获取当前目录中所有文件的列表。
The test
or []
command/builtin has an option to test if a file is a directory.
的test
或[]
命令/内建有一个选项来测试,如果一个文件是一个目录。
Apply recursion and you're done.
应用递归,你就完成了。
回答by pavium
The du
command will list subdirectories recursively.
该du
命令将递归列出子目录。
I'm not sure if emptydirectories get a mention, though
不过,我不确定是否提及空目录
回答by ephemient
Technically, neither findnor lsare used by find2perl|perlor File::Finddirectly.
从技术上讲,没有发现也不LS被使用find2perl| perl或File::Find直接。
$ find2perl -type d | perl $ perl -MFile::Find -e'find(sub{-d&&print"$File::Find::name\n"},".")'
回答by Alex Brown
$ function f { for i in /*; 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
回答by Benjamin W.
Based on this answer; use shell optionsfor the desired globbing behaviour:
- enable
**
withglobstar
(Bash 4.0 or newer) - include hidden directories with
dotglob
- expand to the empty string instead of
**/*/
if there is no match withnullglob
- 使
**
用globstar
(击4.0或更高版本) - 包括隐藏目录
dotglob
- 扩展到空字符串而不是
**/*/
如果没有匹配nullglob
and then use printf
with the %q
formatting directiveto quote directory names with special characters in them:
然后用printf
与%q
格式化指令与他们特殊字符引用目录名称:
shopt -s globstar dotglob nullglob
printf '%q\n' **/*/
so if you have directories like has space
or even containing a newline, you'd get output like
所以如果你有类似has space
甚至包含换行符的目录,你会得到类似的输出
$ printf '%q\n' **/*/
$'has\nnewline/'
has\ space/
with one directory per line.
每行一个目录。