Diferença entre rm meu link simbólico e rm meu link simbólico/

Diferença entre rm meu link simbólico e rm meu link simbólico/

Só queria saber por que, se eu usá- rm -rf my-symlinklo, exclui apenas o link simbólico, mas rm -rf my-symlink/exclui os arquivos do diretório vinculado e deixa o link simbólico em paz?

Responder1

Você pode ver a diferença comparando a saída de stat my-symlinke stat my-symlink/. my-symlink, sem a barra, é o próprio link simbólico; my-symlink/, com a barra, é o diretório apontado pelo link simbólico, que você pode verificar separadamente comparando os inodes do my-symlink/diretório para o qual ele aponta.

Com essas informações em mãos, o comportamento que você está vendo corresponde ao descrito emrmespecificação de: ao processar um link simbólico, rmexclui o link sem descer “para dentro” dele se apontar para um diretório; ao processar um diretório (com a -ropção), ele exclui seu conteúdo recursivamente. No my-symlink/caso, rmtenta deletar o “diretório”, mas falha porque não é um diretório e sim um link simbólico — porém por causa do -fflag isso não causa erro.

Responder2

Pensei em investigar um pouco mais o comportamento, então aqui está outra resposta.

Internamente, rmusaServiço Fiscal Federalpara recursivo em hierarquias de arquivos. fts_openusa uma matriz de caminhos como parâmetro e cria uma estrutura de árvore para cada caminho. Isso permite ao programador explorar vários locais aparentemente como se fizessem parte de uma hierarquia unificada.

Aqui está um programa de teste que você pode usar para jogar com o FTS.

#include <stdio.h>
#include <stdlib.h>
#include <fts.h>

int main(int argc, char* argv[])
{
    if(argc < 2) return EXIT_FAILURE;

    char* const* arr = argv + 1;
    FTS* hier = fts_open(arr, FTS_NOSTAT | FTS_PHYSICAL, NULL);

    FTSENT* ent;
    while((ent = fts_read(hier))) {
        printf("%s info=%d (D=%d DP=%d F=%d SL=%d)\n",
               ent->fts_accpath, ent->fts_info,
               ent->fts_info == FTS_D, ent->fts_info == FTS_DP,
               ent->fts_info == FTS_F || ent->fts_info == FTS_NSOK,
               ent->fts_info == FTS_SL);
    }

    fts_close(hier);
    return EXIT_SUCCESS;
}

Vamos supor que criamos a seguinte estrutura de diretórios:

$ mkdir dir
$ touch dir/file
$ ln -s dir sym

Agora, vamos considerar o seu primeiro caso e ver como o FTS lidera a exploração.

$ gcc fts.c 
$ ./a.out sym
sym info=12 (D=0 DP=0 F=0 SL=1)

Como você pode ver, neste caso, symé visto como um arquivo. Um link simbólico, para ser mais exato. Com essas informações, rmportanto, tratá-lo-ia como um arquivo e chamaria unlinkat(AT_FDCWD, "sym", 0). O último parâmetro (0) faz com unlinkatque se comporte como unlink. Em outras palavras: simplesmente exclui um arquivo. Como resultado, seu link desaparece.

Agora, vamos dar uma olhada no que acontece com sym/.

$ ./a.out sym/
sym/ info=1 (D=1 DP=0 F=0 SL=0)
file info=11 (D=0 DP=0 F=1 SL=0)
sym/ info=6 (D=0 DP=1 F=0 SL=0)

Neste caso, symfoi tratado como seu diretório de destino. Primeiro iteramos para syme sym/filedepois symnovamente. Este último se deve ao modo como o FTS funciona: primeiro, ele itera sobre o conteúdo e depois volta ao nó raiz. Na verdade, isso é muito conveniente para rm. Na primeira passagem ( D), pode apagar arquivos, e na segunda ( DP) remover os diretórios vazios.

Como você pode ver, neste caso, o STF reporta sym/como sendo um diretório em ambos os casos. Isso ocorre porque fornecemos o caminho com uma barra final, o que força o kernel a interpretá-lo como um diretório. No caso de um link, isso significa que ele o seguirá de qualquer maneira.Em termos mais técnicos, as especificações dizem:

Um nome de caminho que contém pelo menos um caractere diferente de barra e que termina com uma ou mais barras finais deve ser resolvido como se um único caractere de ponto ( '.' ) fosse anexado ao nome do caminho.

Como o FTS reporta sym/como um diretório, rmele se comporta como se estivesse excluindo um diretório vazio. Assim, ele chama unlinkat(AT_FDCWD, "sym/", AT_REMOVEDIR). Isso faz com unlinkatque se comporte como rmdir.

Porém, ao resolver o sym/caminho, a unlinkatchamada do sistema perceberá que não está, de fato, recebendo um diretório. Portanto, ele reportará ENOTDIR, o que aciona:

$ rm: cannot remove ‘sym/’: Not a directory

E na verdade, se você remover a -fsinalização das suas chamadas... É exatamente isso que você verá. Agora, se isso é um bug ou um recurso... não tenho ideia.

informação relacionada