Por que -r é necessário recursivo ao copiar um diretório no Linux?

44

Minha pergunta é por que é necessário usar o sinalizador -r (recursivo) ao fazer uma cópia de um diretório? Por que isso:

$ cp -r dir1 copyDir1

Quando eu não desejaria esse comportamento ao copiar um diretório?

Não é uma cópia recursiva de um diretório, na verdade, o comportamento "padrão"; o comportamento que queremos quase o tempo todo?

Parece que esta é uma bandeira supérflua.

    
por Madeleine P. Vincent 13.11.2014 / 19:51

8 respostas

58

A forma como os sistemas de arquivos funcionam, um diretório não é realmente uma pasta contendo arquivos, mas um diretório é um arquivo que contém ponteiros inode para arquivos “filhos” conectados a ele. Ou seja, do ponto de vista do sistema de arquivos, um arquivo é um arquivo, mas um diretório é apenas um arquivo que contém uma lista de arquivos conectados.

Então, na perspectiva da linha de comando, fazendo isso:

$ cp dir1 copyDir1

Basicamente significaria copiar o arquivo chamado, dir1 para um novo arquivo chamado copyDir1 . E no que diz respeito ao sistema de arquivos, dir1 é apenas um arquivo; o fato de ser um "diretório" só será aparente quando o sistema de arquivos realmente verificar dir1 para ver o que essa pilha de bits realmente é.

O sinal -r diz ao sistema de arquivos para recursivamente percorrer a árvore de diretórios / arquivos e copiar qualquer & todo o conteúdo que pode ser um "filho" desse arquivo para um novo lugar.

Agora, por que isso pode parecer supérfluo ou redundante, isso se resume a métodos históricos de lidar com sistemas de arquivos. Além de criar um sistema seguro contra todos os tipos de erros relacionados ao usuário; acidental e intencional.

Ou seja, digamos que você tenha um arquivo ~/bin no seu diretório pessoal que deseja copiar, mas acidentalmente tenha omitido o ~ - porque você é humano e comete erros - então é apenas /bin assim:

cp /bin/ ~/copy_of_bin

Com a "rede de segurança" de /bin sendo um diretório combinado com a necessidade do -r , você evitará copiar acidentalmente toda a raiz binária do sistema no seu diretório pessoal. Se essa rede de segurança não existisse, um desastre menor - ou possivelmente maior - aconteceria.

A lógica aqui é que nos dias pré-GUI (interfaces gráficas com o usuário) as convenções lógicas / comportamentais precisam ser configuradas para evitar ter contratempos criados pelo usuário que possam potencialmente matar um sistema. E usar o -r flag agora é um deles.

Se isso parece supérfluo, então não precisa procurar mais do que o moderno sistema de GUI que pode ser colocado acima dos sistemas de arquivos do Linux. Uma GUI aborda problemas básicos de usuários como este, permitindo arrastar e soltar arquivos e diretórios com facilidade.

No entanto, no caso do domínio das interfaces baseadas em texto, muitas das “experiências do usuário” dentro desse mundo são basicamente apenas lombadas de estradas lógicas e hueristicas que ajudam a manter o usuário sob controle para que desastres em potencial possam ser evitados.

Da mesma forma, é por isso que os sistemas de arquivos Linux / Unix não têm 777 permissões e sudo direitos definidos por padrão e o quanto os administradores de sistemas reais mudam quando um usuário define 777 permissões ou concede a todos sudo direitos. Essas são as coisas básicas que se faz para garantir que o sistema seja estável e “à prova do usuário” quanto possível; qualquer um que se apresse em curto-circuito com essas convenções provavelmente causará danos ao seu sistema sem nem mesmo saber disso.

INFORMAÇÃO ADICIONAL: Outra resposta aqui no site Unix Stack Exchange dá uma boa explicação por que uma cópia não recursiva de um diretório é problemática; a ênfase é minha.

Well, without the -R flag, it's only possible to copy files, because it's rather unusual that someone wants to non-recursively copy a directory: A non-recursive copy would just result in a second name for the directory, pointing to directly the same directory structure. Because that's rarely what people want, and there is actually a separate program that does this (ln), a non-recursive copy of directories is not allowed.

Portanto, se um diretório é realmente um arquivo com itens inode dentro dele, fazer uma cópia direta desse arquivo seria o equivalente a como um link físico funcionaria. Que não é o que alguém quer.

    
por 13.11.2014 / 20:55
18

É bem verdade que esse é o comportamento que queremos quase o tempo todo. Isso não significa necessariamente que a cópia recursiva seja o comportamento padrão.

Acho que as razões cp funcionam porque tem raízes na filosofia Unix . O Unix favorece programas que fazem uma coisa e o fazem bem , bem como programas que são simples em interface e implementação (às vezes chamado de pior é melhor ).

A chave do quebra-cabeça aqui é perceber que cp não copia diretórios - cp copia arquivos (e somente arquivos). Se você deseja copiar um diretório, cp chama-se recursivamente , para copiar os arquivos em cada diretório.

É claro que a diferença entre "copiar diretórios" e "copiar arquivos recursivamente" não é absolutamente nada, do ponto de vista do usuário, mas ter essa interface ajuda a implementação a permanecer simples .

Se você tornou o cp capaz de copiar diretórios, logo se sentiria tentado a adicionar mais recursos que só fazem sentido para diretórios - por exemplo, talvez queira copiar apenas nomes de arquivos que terminem em .sh . Inevitavelmente, isso leva ao inchaço e recurso creep que estamos acostumados em outros sistemas operacionais - tornando o software lento, complexo e propenso a erros.

Outra vantagem é que ter -r também ajuda o usuário a entender o que realmente está acontecendo embaixo da interface. Um bom efeito colateral disso é que aprender o conceito de operação recursiva poupará algum trabalho quando você aprender sobre outras ferramentas que o suportam (como grep , por exemplo)

Algumas pessoas certamente lhe dirão que expor detalhes de implementação ao usuário é ruim e que ter mais recursos é bom . Minha intenção aqui é apenas explicar a lógica desse comportamento, por isso não vou tentar argumentar de qualquer forma.

    
por 14.11.2014 / 15:42
4

As interações com diretórios garantem que você esteja interagindo com um diretório e NÃO com um único arquivo.

Por exemplo:

$ tree
.
└── folder1
    └── sub1
        └── subsub1

3 directories, 0 files
$
$ cp folder1/ folder2
cp: folder1/ is a directory (not copied).
$
$ mkdir blah
$ cp blah/ blah2
cp: blah/ is a directory (not copied).
$ rm blah/
rm: blah/: is a directory

Assim, se você quiser copiar uma pasta com sucesso, já que isso implica tanto a pasta quanto os objetos relacionados à referência da pasta, você deve tratá-la como se fosse uma coleção de arquivos:

$ cp -r folder1/ folder2
$ rm -rf folder1
    
por 13.11.2014 / 20:40
3

A consequência de mudar o padrão seria que milhares de scripts de shell seriam quebrados. Isso leva aos requisitos POSIX e SUS para o comportamento padrão conhecido.

O motivo é o desenvolvimento histórico dos comandos cp, ln e mv (todos os mesmos binários na maioria dos sistemas UNIX antigos) em vários ramos UNIX. Quando -r apareceu (o início cp não tinha uma opção para copiar diretórios; aqui é uma página man cp inicial sem -r ou -R ), existem várias diferenças na manipulação de arquivos especiais, links simbólicos ee outros caprichos do sistema de arquivos.

De Especificações do grupo aberto Especificações 7 :

Earlier versions of this standard included support for the -r option to copy file hierarchies. The -r option is historical practice on BSD and BSD-derived systems. This option is no longer specified by POSIX.1-2008 but may be present in some implementations. The -R option was added as a close synonym to the -r option, selected for consistency with all other options in this volume of POSIX.1-2008 that do recursive directory descent.

The difference between -R and the removed -r option is in the treatment by cp of file types other than regular and directory. It was implementation-defined how the - option treated special files to allow both historical implementations and those that chose to support -r with the same abilities as -R defined by this volume of POSIX.1-2008. The original -r flag, for historic reasons, did not handle special files any differently from regular files, but always read the file and copied its contents. This had obvious problems in the presence of special file types; for example, character devices, FIFOs, and sockets.

Na verdade, você ainda verá algumas pessoas usando regularmente:

cd dir1 ; tar -cf - . | (cd dir2 ; tar -xpf -)

Porque eles não confiam que a implementação cp -r seja com o que eles estão acostumados em uma máquina arbitrária; Ou porque eles querem o comportamento tar .

    
por 14.11.2014 / 21:33
3

Pode ser uma interface de usuário abaixo do ideal hoje, mas foi uma decisão tomada em algum momento por volta de 1970 durante o design do UNIX, quando o disco era consideravelmente mais caro. Milhões de scripts de shell dependem disso, e é tarde demais para alterá-lo.

Veja este artigo para obter informações sobre o design original.

    
por 14.11.2014 / 16:57
3

Uma clara vantagem do sinal -r é que você pode cp * /target/dir copiar apenas todos os arquivos no diretório de origem para o diretório de destino, omitindo (embora com um aviso) todos os diretórios contidos lá no. cp -r * /target/dir copia tudo, incluindo subdiretórios.

    
por 14.11.2014 / 18:18
2

Você precisa desse sinalizador apenas quando cp for um comando para copiar arquivos e diretórios e não apenas diretórios.

Se houvesse um comando especial para copiar diretórios, o comportamento "padrão" seria certamente uma cópia recursiva.

    
por 13.11.2014 / 20:44
1

Como outros já mencionaram, um diretório é basicamente apenas outro tipo de arquivo (em oposição a um arquivo regular), que geralmente "contém" (aponta para) outros arquivos. Pode conter subdiretórios, para os quais o mesmo se aplica ...

Então, se você estiver copiando um diretório (perspectiva do usuário), você está realmente copiando um monte de arquivos (perspectiva do sistema de arquivos) (arquivos regulares, arquivos de diretórios, links simbólicos, ...) e para cada arquivo de diretório Repetindo recursivamente esse processo. Como copiar um diretório é, por definição, um processo recursivo, o argumento do cp é chamado --recursive .

É claro que é muito fácil criar um atalho de comando em seu ambiente de usuário (coloque isso em seu arquivo .profile / .bashrc para torná-lo permanentemente disponível):

alias cpr='cp -r'

Ou talvez melhor:

alias cpa='cp -av'

Dessa forma, você pode copiar um diretório usando cpa dir1 copyDir1 e ele não apenas imprimirá o que está sendo copiado, mas também aplicará as permissões de arquivo.

E como alguém mencionou que o cp poderia, teoricamente, detectar que o arquivo de origem é um diretório e perguntar se deve copiá-lo recursivamente, segue uma sugestão rápida:

cp()
{
    if [ ! -e "$1" ]; then
        echo missing source file
        return 1
    fi
    arg="-d --preserve=all -v"
    if [ -d "$1" ]; then
        read -p "Copy directory recursively? " -n 1 -r
        if [ "$REPLY" == "y" ]; then
            arg="$arg -r"
        fi
        echo
    fi
    /usr/bin/cp $arg "$@"
}

Este é apenas um invólucro cp barato. Ele sempre preserva todos os metadados (isto é, copia o tempo de modificação do arquivo, copia apropriadamente links simbólicos e assim por diante) e, se você estiver tentando copiar um diretório, ele pergunta se deve copiá-lo (recursivamente).

    
por 14.11.2014 / 13:32

Tags