Falha de segmentação ao chamar uma função recursiva bash

2

Eu tenho centenas de várias pastas que contém milhares de arquivos zip que contêm aninhados nos arquivos zip, como mostra os três abaixo

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

Eu criei um pequeno script abaixo do qual verifique a pasta e o conteúdo de forma recursiva, e se encontrar algum arquivo zip, ele extrai o conteúdo e continua a verificar o conteúdo da pasta extraída.

Quando tento executar o script abaixo:

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

No entanto, quando executo o script acima, estou recebendo o erro:

Segmentation fault (core dumped)

Por favor, ajude o que poderia ser o problema.

    
por Noel Alex Makumuli 18.07.2016 / 16:53

2 respostas

3

Existem muitas razões para uma falha de segmentação. A causa mais comum de baixo nível é que o processo tentou acessar um endereço de memória que não está definido, isto é, um desreferenciamento de ponteiro inválido. Isso geralmente é um bug no programa.

Aqui, você está executando um programa shell. O shell é uma linguagem de programação de alto nível, sem ponteiros, portanto, seu script não pode causar uma desreferência de ponteiro inválida como tal.

Muitos programas têm espaço limitado para sua pilha de chamadas e morrem de uma falha de segmentação quando o tamanho da pilha é excedido. Na maioria dos casos, o limite de tamanho da pilha é grande o suficiente para qualquer dado razoável, mas uma recursão infinita pode explodir a pilha.

No bash, a recursão infinita em uma chamada de função causa uma falha de segmentação. (O mesmo vale para traço e mksh; o ksh e o zsh são mais inteligentes e aplicam uma profundidade máxima de aninhamento de chamadas de função no nível do shell para que não sejam segmentados).

Seu script tem vários bugs. O que está te mordendo é que, no caso de um arquivo regular, você sempre chama recurse no final, enquanto você claramente queria fazê-lo apenas para arquivos zip.

Não use && ou || quando você quer dizer if . É mais claro escrever o que você quer dizer; a brevidade através da obscuridade não é uma boa ideia e aqui você mordeu.

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

Outro erro é que você está perdendo aspas duplas em torno de substituições de variáveis , assim o seu programa irá sufocar em nomes de arquivos contendo espaço em branco (entre outros). Sempre use aspas duplas em torno de substituições de variáveis, a menos que você saiba que precisa desativá-las.

Use a expansão de parâmetro em vez de chamar basename e dirname . É mais fácil lidar com casos especiais (por exemplo, nome de arquivo que começa com - ) e é mais rápido.

Outro erro que eu encontrei é que o padrão +(sh|xslt|dtd|log|txt) é claramente destinado a ser @(sh|xslt|dtd|log|txt) (corresponde a essas extensões, não shsh , dtdtxtshdtd etc.).

Aqui está o caso de arquivo normal, com os erros acima corrigidos e reescritos com case para maior clareza:

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

Note que não verifiquei a lógica ou testei o código. Esta parece ser uma maneira complicada de escrever

find -type f -name '*.zip' -exec sh -c 'unzip -uq "$0" -d "${0%.zip}"' {} \;
    
por 19.07.2016 / 02:37
0

De resposta de Gilles :

In bash, infinite recursion in a function call does cause a segmentation fault. (The same goes for dash and mksh; ksh and zsh are smarter and apply a maximum function call nesting depth at the shell level so that they don't segfault.)

No Bash, você também pode definir a profundidade máxima de aninhamento de chamadas de função, definindo FUNCNEST . Isso é descrito em man bash :

The FUNCNEST variable, if set to a numeric value greater than 0, defines a maximum function nesting level. Function invocations that exceed the limit cause the entire command to abort.

Aqui você pode ver isso em ação:

$ f () { f; }
$ FUNCNEST=10 f
bash: f: maximum function nesting level exceeded (10)
    
por 25.01.2018 / 12:43