以下是一种可能的实现方式:
# 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(即,如果test XYZ 成功,则执行command)。还要注意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(数字比较。)
另一种技术是生成子shell,而不是使用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 实现中,可以作为参数传递给 for(或任何内置或外部命令)的字符数有硬性限制(~4k)。 shell expands, inline, * to a list of all matching filenames 在实际执行 for 之前,如果 * 在包含大量文件的目录中展开,您可能会遇到麻烦(运行时会遇到同样的麻烦,例如 ls *目录,例如得到类似Command too long的错误。)