재귀 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

분할 오류는 여러 가지 이유로 발생할 수 있습니다. 가장 일반적인 하위 수준 원인은 정의되지 않은 메모리 주소에 액세스하려는 프로세스, 즉 잘못된 포인터 역참조입니다. 이는 일반적으로 프로그램의 버그입니다.

여기서는 쉘 프로그램을 실행하고 있습니다. 셸은 고급 프로그래밍 언어이며 포인터가 없으므로 스크립트에서 잘못된 포인터 역참조가 발생할 수 없습니다.

많은 프로그램이 공간이 제한되어 있습니다.호출 스택스택 크기가 초과되어 발생한 분할 오류입니다. 대부분의 경우 스택 크기 제한은 합리적인 양의 데이터에 대해 충분히 크지만 무한 재귀로 인해 스택이 손상될 수 있습니다.

Bash에서 함수 호출의 무한 재귀는 실제로 분할 오류를 일으킬 수 있습니다. (dash와 mksh도 마찬가지입니다. ksh와 zsh는 더 똑똑하고 쉘 수준에서 최대 함수 호출 중첩 깊이를 적용하므로 세그폴트가 발생하지 않습니다.)


스크립트에 몇 가지 오류가 있습니다. 당신을 괴롭히는 것은 일반 파일의 경우 항상 recursezip 파일에 대해서만 호출하고 싶을 때 항상 마지막에 호출한다는 것입니다.

또는 을 의미할 때는 사용하지 마세요 &&. 당신이 의미하는 바를 쓰는 것이 더 명확합니다. 모호함을 통한 간결함은 좋은 생각이 아니며 여기서는 당신을 귀찮게 합니다.||if

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

또 다른 오류는 당신이 누락되었다는 것입니다변수 대체는 큰따옴표로 묶입니다., 따라서 프로그램은 무엇보다도 공백이 포함된 파일 이름을 차단합니다. 변수 대체를 생략해야 한다는 것을 알지 않는 한 항상 큰따옴표를 사용하십시오.

basename및 를 호출하는 대신 매개변수 확장을 사용하세요 dirname. 특수한 경우(예: 로 시작하는 파일 이름 -)를 처리하는 것이 더 쉽고 빠릅니다.

내가 발견한 또 다른 버그는 패턴이 +(sh|xslt|dtd|log|txt)분명히 의미하는 것입니다 @(sh|xslt|dtd|log|txt)(이러한 확장자와 일치하는 shsh등이 아님 dtdtxtshdtd).

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는 더 똑똑하고 쉘 수준에서 최대 함수 호출 중첩 깊이를 적용하므로 세그폴트가 발생하지 않습니다.)

Bash에서는 설정을 통해 최대 함수 호출 중첩 깊이를 설정할 수도 있습니다 FUNCNEST. 이 위치는 다음과 같습니다 man bash.

FUNCNEST 변수가 0보다 큰 값으로 설정되면 최대 함수 중첩 수준을 정의합니다. 제한을 초과하는 함수 호출로 인해 전체 명령이 중단됩니다.

여기에서 실제로 작동하는 모습을 볼 수 있습니다:

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

관련 정보