调用递归 bash 函数时出现分段错误

调用递归 bash 函数时出现分段错误

我有数百个多个文件夹,其中包含数千个 zip 文件,这些文件包含嵌套在 zip 文件中,如下面三个所示

start tree structure
012016/
├── 2016-01
│   └── 2016-01
│       ├── build
│       ├── DOC
│       │   ├── WONWA1
│       │   │   ├── WO1NWA1
│       │   │   │   ├── WO2016000001NWA1.xml
│       │   │   ├── WO1NWA1.zip
│       │   │   ├── WO2NWA1
│       │   │   │   ├── WO2016000002NWA1_tr.xml
│       │   │   ├── WO2NWA1.zip
└── 2016-01.zip

end tree structure

我在下面创建了一个简短的脚本,它递归地检查文件夹和内容,如果找到任何 zip 文件,它就会提取内容,然后继续检查提取的文件夹的内容。

当我尝试运行下面的脚本时:

recurse() {
    for i in "$1"/*;
    do
        currentItem="$i"
        extension="${currentItem##*.}"

        if [ -d "$i" ]; then
            #echo "dir: $i"
            recurse "$i"
        elif [ -f "$i" ];   then
            #echo "file: $i"
            #echo "ext: $extension"

            [[ ${extension} = +(sh|xslt|dtd|log|txt) ]] && break

            extractionDirectory=$(dirname $currentItem)/$(basename -s .zip $currentItem )

            [[ ${extension} = "zip" ]] && unzip -uq $currentItem -d "${extractionDirectory}"

            recurse ${extractionDirectory}
        fi
    done }
    recurse $PWD

但是,当我运行上面的脚本时,我收到错误:

分段错误(核心转储)

答案1

造成分段错误的原因有很多。最常见的低级原因是进程试图访问未定义的内存地址,即无效的指针取消引用。这通常是程序中的错误。

在这里,您正在运行一个 shell 程序。 shell 是一种高级编程语言,没有指针,因此您的脚本不会导致无效的指针取消引用。

许多程序的空间有限调用栈分段错误的死因是超出了堆栈大小。在大多数情况下,堆栈大小限制对于任何合理的数据来说都足够大,但无限递归可能会破坏堆栈。

在 bash 中,函数调用中的无限递归确实会导致分段错误。 (dash 和 mksh 也是如此;ksh 和 zsh 更聪明,并且在 shell 级别应用最大函数调用嵌套深度,这样它们就不会出现段错误。)


您的脚本有几个错误。令您困扰的是,对于常规文件,您总是recurse在最后调用,而您显然只想对 zip 文件执行此操作。

当您的意思是 时,不要使用&&or 。写出你的意思就更清楚了;通过晦涩难懂来简洁并不是一个好主意,它在这里让你感到困扰。||if

if [[ ${extension} = "zip" ]]; then
  unzip -uq $currentItem -d "${extractionDirectory}"
  recurse ${extractionDirectory}
fi

另一个错误是你失踪了变量替换用双引号引起来,因此您的程序将因包含空格(以及其他)的文件名而阻塞。始终在变量替换周围使用双引号,除非您知道需要将其省略。

使用参数扩展而不是调用basenameand dirname。处理特殊情况更容易(例如以 开头的文件名-)并且速度更快。

我碰巧发现的另一个错误是该模式+(sh|xslt|dtd|log|txt)显然意味着@(sh|xslt|dtd|log|txt)(匹配这些扩展名,而不是shshdtdtxtshdtd)。

case这是常规文件情况,为了清楚起见,修复并重写了上述错误:

case "$extension" in
  sh|xslt|dtd|log|txt) break;;
  zip)
    extractionDirectory=$"{currentItem%.zip}"
    unzip -uq "$currentItem" -d "${extractionDirectory}"
    recurse "${extractionDirectory}"
esac

请注意,我尚未验证逻辑或测试代码。这似乎是一种复杂的写作方式

find -type f -name '*.zip' -exec sh -c 'unzip -uq "$0" -d "${0%.zip}"' {} \;

答案2

吉尔斯的回答:

在 bash 中,函数调用中的无限递归确实会导致分段错误。 (dash 和 mksh 也是如此;ksh 和 zsh 更聪明,并且在 shell 级别应用最大函数调用嵌套深度,这样它们就不会出现段错误。)

在 Bash 中,您还可以通过设置来设置最大函数调用嵌套深度FUNCNEST。这在man bash

如果将 FUNCNEST 变量设置为大于 0 的数值,则定义最大函数嵌套级别。超过限制的函数调用会导致整个命令中止。

您可以在这里看到它的实际效果:

$ f () { f; }
$ FUNCNEST=10 f
bash: f: maximum function nesting level exceeded (10)

相关内容