如何在Bash中递归列出子目录,而不使用"find"或"ls"命令?
如何在Bash中递归列出子目录而不使用"find"或"ls"命令?
在上面的内容中,我们可以看到一个可能的实现。这是一个名为"my_ls"的函数,它可以递归地列出给定目录的内容和子目录。该函数的参数有两个,第一个参数是要列出内容的目录,第二个参数是缩进。
该函数的实现过程如下:
- 首先,保存当前目录并切换到传入的目录。
- 然后,对于每个非隐藏的文件或目录,打印文件或目录的名称。
- 如果是目录,则进入该目录并继续递归列出其内容。
- 最后,恢复之前保存的目录。
通过调用"my_ls .",我们可以递归列出当前目录及其子目录中的文件。
如果要修改上述脚本以打印文件的完整路径而不只是缩进的文件/目录名称,可以考虑去掉"pushd"和"popd"命令,并且不再需要第二个参数"$2"。
另外,注意到代码中使用了"test XYZ && command"的形式,它与"if test XYZ ; then command ; fi"完全等价。"test XYZ"也等价于"[ XYZ ]",因此上述代码还等价于"if [ XYZ ] ; then command ; fi"。
如果移除"test -e "$file" &&"条件(只保留"echo"),可以看到发生了什么。
如果移除对"$file"的双引号,并且目录中的文件名包含空格,可以看到发生了什么。可以在脚本的顶部添加"set -x"来打开调试输出,并详细查看发生了什么(可以将调试输出重定向到文件,运行"sh -x scriptname.sh 2>debugoutput.txt")。
为了列出隐藏文件(如".bashrc"),可以在"for"循环中添加".?*"的模式,然后在判断中排除".."。
还可以使用子shell来代替"pushd"/"popd"的使用。
需要注意的是,在某些shell实现中,对于"for"(或任何内置命令或外部命令)的参数有一个硬限制(大约4k个字符)。由于shell会在实际执行"for"之前将"*"扩展为匹配的文件名列表,所以如果在具有大量文件的目录中扩展"*",可能会遇到问题(与在相同目录中运行"ls *"时遇到的问题相同,比如出现"Command too long"的错误)。
如果要列出隐藏文件,可以使用"shopt -s dotglob"命令。但这个命令不是可移植的。使用".*"(或".?*"以跳过".")即可达到相同的效果,并且是可移植的。
问题的出现原因:该问题是因为需要在Bash中递归列出子目录,但是不能使用"find"或"ls"命令。
解决方法1:
#!/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
解决方法2:
#!/bin/bash shopt -s globstar for file in /path/** do echo $file done
这些方法也是有效的答案,并且可以避免使用"find"或"ls"命令。
然而,第一种方法在目录为空的情况下会失败,会打印出"dir: bla/*",因为"bla/*"被视为字面量,没有子目录。可以通过使用"pushd/popd"命令,或在"do"之后的第一行添加"if [[ "$1/*" = "$i" ]]; then continue; fi"来解决此问题。
第二种方法也可以通过使用"shopt -s nullglob"或在循环内部使用"[ -e "$i" ] || [ -L "$i" ] || continue"来修复,以避免输出"bla/*"。
因此,以上两种方法都可以实现在Bash中递归列出子目录的功能。