在Bash中递归列出给定目录中的文件

6

我知道可以使用 ls -R path 命令实现。但是我正在学习Shell语言的语法和控制结构,因此我尝试编写自己的代码:

#!/bin/sh

arg=$1;

lsRec() {
    for x in $1*; do
        if [ -d "$x" ]; then
            lsRec $x;
        else
            echo "$x";
        fi
    done
}

lsRec $arg;

当我调用命令 ./ej2.sh ~/Documents/ 时,终端会抛出错误:segmentation fault (core dumped)。为什么会出现这个错误?我的代码有什么问题吗?
谢谢。

2
顺便提一句:您标记了问题为“bash”,但您的 shebang 行针对的是“sh”,并且您正在使用(仅)“sh”兼容的语法。 - mklement0
2
另外一点:只有当您在同一行上放置多个语句时,才需要使用 ; 终止语句(但请注意,如果将 dothen 放置在同一行上,则需要在前面加上 ;)。 - mklement0
3个回答

8
你的算法进入了无限循环,因为lsRec函数隐式地期望其参数以“/”结尾。第一层工作正常,因为你将以“/”结尾的路径作为输入传递,但第二层不行,因为你用于递归调用的路径没有以“/”结尾。你可以在进行递归调用时添加斜杠,使其看起来像lsRec $x/,或者更好的方法是在循环参数中添加斜杠,如for x in $1/*; do(因为系统通常会忽略多个相邻的路径分隔符)。
接下来,我建议你引用值(例如for x in "$1/"*lsRec "$x"lsRec "$arg"),以避免路径包含空格字符时出现问题。当你在正在扫描的目录层次结构下创建一个名字中带有空格的目录时,你就会明白这一点。

5
问题在于"for x in $1*"会匹配到$1, 如果这样说有意义的话?所以它会变成一个无限循环。有两个解决方案:
  • 检查x是否等于$1
  • 将for循环更改为"for x in $1/*"
因为$1会被替换为传递给函数的参数,所以如果它发送了"hello",那么它就会变成"for x in hello*"。 现在,这是一个globbing模式,会选择"hello",从而导致无限循环。
第二种解决方案可行是因为"hello"变成了"hello/*"而不是"hello*"。
这段代码对我来说工作正常:
#!/bin/sh

arg=$1;

lsRec() {
    for x in "$1"/*; do
        echo "$x"
        if [ -d "$x" ]; then
            echo "This is a directory:"
            lsRec "$x";
        else
            echo "This is not a directory:"
            echo "$x";
       fi
    done
}

lsRec "$arg";

希望这有所帮助!

3
斜杠还是没有出现? :) - mike.dld
1
@mike.dld 哦,没问题了。谢谢啦! - archivetherexus
1
@mklement0 现在好了吗? - archivetherexus

2
我认为你创建了一个分叉炸弹。你的代码创建了一个无限递归。
你应该将代码更改为:
#!/bin/sh
   arg=$1;

   lsRec() {
      for x in $1/*; do
        if [ -d "$x" ]; then
            echo "$x"  ## here you print the line
            lsRec $x; ## here you pass the contents and NOT the line itself
                        ## your programm was passing dirs without processing
                        ## over and over
        else
            echo "$x";
        fi
    done
}

lsRec $arg;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接