我知道您可以使用find
命令完成这项简单的工作,但我的任务是不使用find
或ls
来完成工作。我怎么能这样做?
答案 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 | ls或find2perl直接使用perl和File::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
}