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

47

Minha pergunta é por que é necessário usar o -rsinalizador (recursivo) ao fazer uma cópia de um diretório? Ou seja, por que fazer isso:

$ cp -r dir1 copyDir1

Quando eu não gostaria desse comportamento ao copiar um diretório?

Uma cópia recursiva de um diretório não é realmente o comportamento "padrão"; o comportamento que queremos quase o tempo todo?

Parece que esta é uma bandeira supérflua.

Madeleine P. Vincent
fonte
Você não precisaria copiar os arquivos e a pasta também?
QuyNguyen2013
Se você acha que isso seria uma melhoria, pode repassar essa solicitação no canal de desenvolvedores. Caso contrário, provavelmente foi programado há muito tempo.
Blogger
@blogger Foi programado há muito tempo, mas por um motivo. Ou seja, se alguém deseja fazer um trabalho básico em um ambiente de linha de comando, sua tarefa deve ser tão fácil quanto evitar a falha do sistema. Significando que existem boas razões para existir algumas convenções de interação do usuário da linha de comando. Amplio esse conceito na minha resposta.
JakeGould
2
Esta questão tem sido feitas e respondidas em unix.SE .
dotancohen
O mesmo vale pararm

Respostas:

58

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

Portanto, da perspectiva da linha de comando, faça o seguinte:

$ cp dir1 copyDir1

Basicamente, significa copiar o arquivo nomeado, dir1para um novo arquivo nomeado 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 dir1para ver o que realmente é essa pilha de bits.

O -rsinalizador diz ao sistema de arquivos para rolar recursivamente a árvore de arquivos / diretórios e copiar todo e qualquer conteúdo que possa ser um "filho" desse arquivo para um novo local.

Agora, por que isso pode parecer supérfluo ou redundante, isso realmente 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 ~/binarquivo em seu diretório pessoal que deseja copiar, mas que acidentalmente tenha deixado de lado, ~porque você é humano e comete erros, então é assim /bin:

cp /bin/ ~/copy_of_bin

Com a “rede de segurança” de /binser um diretório combinado com a necessidade do -rsinalizador, você evitará copiar acidentalmente toda a raiz binária do sistema em que está no diretório inicial. Se essa rede de segurança não existisse, um desastre menor - ou possivelmente maior - aconteceria.

A lógica aqui é que nos dias anteriores às convenções lógicas / comportamentais pré-GUI (interfaces gráficas com o usuário) precisam ser definidas para evitar que o usuário crie contratempos que possam potencialmente matar um sistema. E usar a -rbandeira agora é um deles.

Se isso parecer supérfluo, não será necessário procurar o sistema GUI moderno acima dos sistemas de arquivos Linux. Uma GUI soluciona problemas básicos do usuário como esse, permitindo arrastar e soltar arquivos e diretórios com facilidade.

Porém, no caso das interfaces baseadas em texto, grande parte da “experiência do usuário” nesse mundo é basicamente apenas obstáculos econômicos e lógicos que ajudam a manter o usuário sob controle para evitar possíveis desastres.

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

INFORMAÇÕES ADICIONAIS: Outra resposta aqui no site do Unix Stack Exchange fornece uma boa explicação de por que uma cópia não recursiva de um diretório é problemática; ênfase é minha.

Bem, sem o sinalizador -R, só é possível copiar arquivos, porque é bastante incomum que alguém queira copiar um diretório de forma não recursiva : uma cópia não recursiva resultaria apenas em um segundo nome para o diretório, apontando diretamente para o diretório mesma estrutura de diretórios. Como isso raramente é o que as pessoas querem e, na verdade, existe um programa separado que faz isso (ln), uma cópia não recursiva dos diretórios não é permitida.

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

JakeGould
fonte
19
Pessoalmente, acho que o aspecto "proteção" não passa no teste do olfato. Alguns humanos poderiam digitar cp -r /bintão facilmente quanto cp-r ~/bin. A bandeira em si não evita erros ou necessariamente torna alguém melhor em tomar cuidado. Se você deseja evitar erros, o comando cp pode facilmente olhar para o nó em questão e fornecer um prompt, algo como "Este é um diretório, deseja copiar todo o conteúdo para o local especificado (y / n)? " Isso seria rede de segurança . Exigir -r para diretórios mantém o código inchado mais do que qualquer coisa.
JDL
12
Má analogia com o bueiro. Como o @JDL disse, a bandeira em questão não faz nada para impedir um erro de digitação no caminho. Eu ficaria mais feliz em aceitar a coerência com outros comandos como razão, mas sinto que a verdadeira razão é "foi assim que foi escrita pela primeira vez e agora tantas coisas dependem desse comportamento, é impossível mudar isso".
Básico
7
Quando movemos um diretório, não precisamos de -r. Acho que a resposta vinculada em unix.stackexchange.com é muito mais objetiva. O equivalente a uma cópia não recursiva seria ter um segundo diretório e links físicos para todos os arquivos dentro da árvore de diretórios.
Gerrit
2
Se não me engano, -r era uma extensão do GNU - não acho que o histórico do UNIX cp tivesse uma cópia recursiva - e isso foi parte do motivo do comando rsync .
Mei
2
@JakeGould eu entendo conceitos básicos. No entanto, argumento contra a ideia de que um sinalizador -r no comando em questão fornece qualquer segurança adicional. Pela minha experiência, os comandos linux da linha de comando são historicamente inerentemente inseguros. Segurança foi adicionada horas extras, se houver. A segurança inerente ao design do linux vem do sistema de permissões e é executada como root. Não rodar como root também é algo que é mais uma convenção do que um design. A convenção é suportada na maioria dos novos instaladores Linux, mas nem sempre foi.
JDL
19

É bem verdade que esse é o comportamento que queremos quase o tempo todo. Porém, isso não significa necessariamente que copiar recursivamente deve ser o comportamento padrão.

Eu acho que as razões cpagem, pois têm raízes na filosofia Unix . Unix favorece programas que fazer uma coisa e fazê-lo bem , assim como os programas que são simples, em ambos interface e implementação (às vezes chamado de pior, melhor ).

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

Obviamente, a diferença entre "copiar diretórios" e "copiar arquivos recursivamente" não é absolutamente nada, na perspectiva do usuário, mas ter essa interface ajuda a implementação a permanecer simples .

Se você cpconseguiu copiar diretórios, em breve ficaria tentado a adicionar mais recursos que só fazem sentido para diretórios - por exemplo, convém copiar apenas os nomes de arquivos que terminaram em .sh. Inevitavelmente, isso leva ao inchaço e à fluência de recursos com os quais estamos acostumados em outros sistemas operacionais - tornando o software lento, complexo e propenso a erros.

Outra vantagem é que ter -rtambém ajuda o usuário a entender o que realmente está acontecendo sob a 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 dirão que a exposição dos detalhes da implementação ao usuário é ruim e que ter mais recursos é bom . Minha intenção aqui é apenas explicar a lógica desse comportamento, então não tentarei argumentar de qualquer maneira.

goncalopp
fonte
2
+1 "... faça uma coisa e faça bem ..." Obrigado por declarar isso!
JakeGould
5

As interações com diretórios garantem que você esteja interagindo com um diretório e NÃO apenas 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

Portanto, se você deseja copiar com êxito uma pasta, uma vez que ela implica a pasta e os objetos relacionados à referência à pasta, você deve tratá-la como se fosse uma coleção de arquivos:

$ cp -r folder1/ folder2
$ rm -rf folder1
mbb
fonte
3

A consequência da alteração do padrão seria que milhares de scripts de shell quebrariam. Isso leva aos requisitos POSIX e SUS para o conhecido comportamento padrão.

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árias ramificações do UNIX. Quando -rapareceu (o início cpnão tinha a opção de copiar diretórios; aqui está uma página inicial do cp sem -rou -R), havia várias diferenças no manuseio de arquivos especiais, links simbólicos e outros caprichos do sistema de arquivos.

Do Open Group Base Specifications Edição 7 :

As versões anteriores deste padrão incluíam suporte para a opção -r para copiar hierarquias de arquivos. A opção -r é uma prática histórica em sistemas derivados de BSD e BSD. Esta opção não é mais especificada no POSIX.1-2008, mas pode estar presente em algumas implementações. A opção -R foi incluída como um sinônimo próximo da opção -r, selecionada para consistência com todas as outras opções neste volume do POSIX.1-2008 que fazem descida de diretório recursiva.

A diferença entre -R e a opção -r removida está no tratamento por cp de tipos de arquivo diferentes de regular e diretório. Foi definido pela implementação como a opção - tratava arquivos especiais para permitir implementações históricas e aquelas que optavam por suportar -r com as mesmas habilidades que -R definidas por este volume do POSIX.1-2008. O sinalizador -r original, por razões históricas, não manipula arquivos especiais de maneira diferente dos arquivos regulares, mas sempre lê o arquivo e copia seu conteúdo. Isso teve problemas óbvios na presença de tipos de arquivos especiais; por exemplo, dispositivos de caracteres, FIFOs e soquetes.

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

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

Porque eles não confiam que a cp -rimplementação seja o que estão acostumados em uma máquina arbitrária; Ou porque eles querem o tarcomportamento.

Kael
fonte
3

Pode ser uma interface de usuário subótima hoje, mas foi uma decisão tomada em torno de 1970 durante o design do UNIX, quando o disco era consideravelmente mais caro. Milhões de scripts de shell dependem do funcionamento dessa maneira e é tarde demais para alterá-lo.

Consulte este artigo para obter as informações de design originais.

pjc50
fonte
3

Uma clara vantagem do -rsinalizador é que você pode cp * /target/dircopiar apenas todos os arquivos no diretório de origem no diretório de destino, omitindo (embora com um aviso) todos os diretórios nele contidos. cp -r * /target/dircopiaria tudo, incluindo subdiretórios.

Interneci
fonte
2

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

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

cara
fonte
1
Faz sentido. Mas por que alguém estaria copiando um diretório que não possui pelo menos um arquivo? Por que não usar apenas mkdir?
JakeGould
1
@JakeGould talvez porque eles precisam preservar a propriedade e as permissões?
Ruslan
1

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

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

Obviamente, é muito fácil criar um atalho de comando no ambiente do usuário (coloque isso no 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 copyDir1e ele não apenas imprimirá o que está sendo copiado, mas também aplicará 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 deveria copiá-lo recursivamente, aqui está 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 (ou seja, copia a hora da modificação do arquivo, copia corretamente links simbólicos e assim por diante) e se você estiver tentando copiar um diretório, ele pergunta se deve copiá-lo (recursivamente).

basic6
fonte