Pourquoi -r récursif est-il nécessaire lors de la copie d'un répertoire sous Linux?

47

Ma question est la suivante: pourquoi est-il nécessaire d'utiliser l' -rindicateur (récursif) lors de la copie d'un répertoire? Ie, pourquoi faire ceci:

$ cp -r dir1 copyDir1

Quand ne voudrais-je pas ce comportement lors de la copie d'un répertoire?

Une copie récursive d'un répertoire n'est-elle pas vraiment le comportement «par défaut»; le comportement que nous voulons presque tout le temps?

C'est comme si c'était un drapeau superflu.

Madeleine P. Vincent
la source
Ne devriez-vous pas également copier les fichiers et le dossier?
QuyNguyen2013
Si vous pensez que cela constituerait une amélioration, vous pouvez republier cette demande sur le canal des développeurs. Sinon, cela a probablement été programmé il y a longtemps.
blogueur
@ blogger C'était programmé il y a longtemps, mais pour une raison. Cela signifie que si une personne souhaite effectuer un travail de base dans un environnement de ligne de commande, sa tâche ne doit être aussi simple que d'éviter une défaillance du système. Cela signifie qu'il existe de bonnes raisons pour lesquelles certaines conventions d'interaction utilisateur en ligne de commande existent. Je développe ce concept dans ma réponse.
JakeGould
2
Cette question a été posée et répondue sur unix.SE .
dotancohen
Il en va de même pourrm

Réponses:

58

De la manière dont fonctionnent les systèmes de fichiers, un répertoire n'est pas réellement un dossier contenant des fichiers, mais un répertoire est un fichier contenant des pointeurs inodes vers des fichiers «enfants» qui y sont connectés. Cela signifie que, du point de vue du système de fichiers, un fichier est un fichier, mais un répertoire est simplement un fichier contenant une liste de fichiers connectés.

Donc, du point de vue de la ligne de commande, ceci:

$ cp dir1 copyDir1

Cela signifierait essentiellement copier le fichier nommé dir1dans un nouveau fichier nommé copyDir1. Et en ce qui concerne le système de fichiers, ce dir1n’est de toute façon qu’un fichier; le fait qu'il s'agisse d'un «répertoire» n'apparaîtra que lorsque le système de fichiers vérifiera réellement dir1ce qu'est réellement cette pile de bits.

L’ -rindicateur indique au système de fichiers de dérouler de manière récursive l’arborescence de fichiers / répertoires et de copier tout contenu pouvant constituer un «enfant» de ce fichier dans un nouvel emplacement.

Maintenant, pour ce qui est de savoir pourquoi cela peut sembler superflu ou redondant, cela revient vraiment aux méthodes historiques de gestion des systèmes de fichiers. En plus de créer un système sûr de tous les types d'erreurs liées à l'utilisateur; accidentelle et intentionnelle.

Cela veut dire, disons que vous avez un ~/binfichier dans votre répertoire personnel que vous voulez copier mais que vous avez accidentellement laissé de côté ~- parce que vous êtes un humain et que vous faites des erreurs - et que c'est /bincomme ça:

cp /bin/ ~/copy_of_bin

Avec le «filet de sécurité» d’ /binêtre un répertoire associé à la nécessité du -rdrapeau, vous éviterez de copier accidentellement toute la racine binaire du système que vous utilisez dans votre répertoire personnel. Si ce filet de sécurité n'existait pas, un désastre mineur, voire majeur serait survenu.

La logique est que dans les jours qui précèdent les conventions logiques / comportementales pré-GUI (interfaces graphiques) doivent être définies pour éviter que des incidents créés par l'utilisateur puissent potentiellement tuer un système. Et utiliser le -rdrapeau en fait maintenant partie.

Si cela vous semble superflu, ne cherchez pas plus loin que le système graphique moderne que vous pouvez placer au-dessus des systèmes de fichiers Linux. Une interface graphique répond aux problèmes utilisateur de base comme celui-ci en permettant de glisser-déposer facilement des fichiers et des répertoires.

Mais dans le cas des interfaces textuelles, une bonne partie de «l'expérience utilisateur» au sein de ce monde n'est fondamentalement que des obstacles routiers logiques et hueristiques qui permettent de garder l'utilisateur sous contrôle afin d'éviter un désastre potentiel.

De même, c’est la raison pour laquelle les systèmes de fichiers Linux / Unix ne disposent pas d’ 777autorisations et de sudodroits définis par défaut et de la façon dont les véritables administrateurs système tremblent lorsqu'un utilisateur définit des 777autorisations ou accorde des sudodroits à tous . Ce sont les tâches de base que l’on fait pour s’assurer que le système est stable et aussi «résistant à l’utilisateur» que possible; quiconque se précipitera pour court-circuiter ces conventions causera très probablement des dommages à son système sans même le savoir.

INFORMATIONS SUPPLÉMENTAIRES: Une autre réponse ici sur le site Unix Stack Exchange explique bien pourquoi une copie non récursive d'un répertoire pose problème; l'emphase est à moi.

Eh bien, sans l'indicateur -R, il est uniquement possible de copier des fichiers, car il est plutôt inhabituel que quelqu'un veuille copier un répertoire de manière non récursive: une copie non récursive ne ferait que donner un deuxième nom au répertoire même structure de répertoire. Parce que c'est rarement ce que les gens veulent, et qu'il existe en fait un programme distinct (ln), une copie non récursive de répertoires n'est pas autorisée.

Ainsi, si un répertoire est simplement un fichier contenant des éléments inode, créer une copie directe de ce fichier équivaudrait à la manière dont un lien réel fonctionnerait. Ce qui n'est pas ce que tout le monde veut.

JakeGould
la source
19
Personnellement, je pense que l’aspect "protection" n’est pas satisfaisant. Certains humains pourraient aussi facilement taper cp -r /binque cp-r ~/bin. Le drapeau lui-même n’empêche pas les erreurs et ne rend nécessairement personne plus prudent en matière de prudence. Si vous souhaitez éviter les erreurs, la commande cp peut tout aussi bien examiner le nœud en question et fournir une invite, quelque chose dans le sens de "Ceci est un répertoire, voulez-vous copier tout le contenu à l'emplacement spécifié (y / n)? " Ce serait un filet de sécurité . Le fait d’avoir besoin de -r pour les répertoires réduit le plus possible le fardeau de code.
JDL
12
Mauvaise analogie avec le trou d'homme. Comme @JDL l'a dit, le drapeau en question ne fait rien pour empêcher une faute de frappe dans le chemin. Je serais plus heureux d’accepter la cohérence avec d’autres commandes comme raison, mais j’ai le sentiment que la vraie raison est que "c’est comme ça a été écrit pour la première fois et que tant de choses dépendent de ce comportement, il est impossible de la changer".
base
7
Lorsque nous déplaçons un répertoire, nous n'avons pas besoin de -r. Je pense que la réponse liée à unix.stackexchange.com est beaucoup plus pertinente. L'équivalent d'une copie non récursive serait d'avoir un deuxième répertoire et des liens fixes pour tous les fichiers de l'arborescence.
gerrit
2
Si je ne me trompe pas, -r était une extension GNU (je ne pense pas que l'historique UNIX cp en avait une copie récursive), ce qui explique en partie la raison de la commande rsync .
Mei
2
@JakeGould Je comprends les concepts de base. Toutefois, je suis contre l’idée qu’un indicateur -r sur la commande en question offre une sécurité supplémentaire. D'après mon expérience, les commandes linux en ligne de commande sont historiquement intrinsèquement dangereuses. La sécurité a été ajoutée, le cas échéant. La sécurité inhérente à la conception de linux provient du système d’autorisations et ne s'exécute pas en tant que root. Ne pas fonctionner en tant que root est aussi quelque chose qui est plus une convention qu'un design. La convention est supportée par la plupart des nouveaux installateurs Linux, mais ne l’a pas toujours été.
JDL
19

C'est très vrai que c'est le comportement que nous voulons presque tout le temps. Cela ne signifie toutefois pas nécessairement que la copie récursive devrait être le comportement par défaut.

Je pense que les raisons sont les cpmêmes qu’elles sont enracinées dans la philosophie Unix . Unix privilégie les programmes qui font une chose et qui le font bien , ainsi que les programmes simples à la fois en interface et en implémentation (parfois appelé pire c'est mieux ).

La pièce clé du puzzle ici est de réaliser que cpne copiez pas de répertoires - copiez des cpfichiers (et uniquement des fichiers). Si vous souhaitez copier un répertoire, cp appelez-le récursivement afin de copier les fichiers de chaque répertoire.

Bien sûr, la différence entre "copier des répertoires" et "copier des fichiers de manière récursive" n’est absolument rien, du point de vue de l’utilisateur, mais le fait de disposer de cette interface aide l’implémentation à rester simple .

Si vous cpparveniez à copier des répertoires, vous seriez bientôt tenté d’ajouter d’autres fonctionnalités qui n’auraient de sens que pour les répertoires. Par exemple, vous souhaiterez peut-être ne copier que les noms de fichiers terminés .sh. Inévitablement, cela engendre le flou et le glissement de fonctionnalités auxquels nous sommes habitués dans d’autres systèmes d’exploitation - rendant les logiciels lents, complexes et sujets aux erreurs.

Avoir un autre avantage est que le fait d’ -raider l’utilisateur à comprendre ce qui se passe réellement sous l’interface. Un effet secondaire intéressant de cette méthode est que l’apprentissage du concept d’opération récursive vous évitera du travail lorsque vous en apprendrez sur d’autres outils qui le prennent en charge (comme grep, par exemple).


Certaines personnes vous diront certainement qu'il est mauvais d' exposer les détails de la mise en œuvre à l'utilisateur et qu'il est bon d' avoir plus de fonctionnalités . Mon intention ici est simplement d’expliquer la raison de ce comportement, je ne vais donc pas essayer de discuter d’une façon ou d’une autre.

goncalopp
la source
2
+1 “… faites une chose et faites-le bien…” Merci de l'avoir dit!
JakeGould
5

Les interactions avec les répertoires vous permettent de savoir que vous interagissez avec un répertoire et non pas avec un seul fichier.

Par exemple:

$ 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

Donc, si vous voulez réussir à copier un dossier, puisqu'il implique à la fois le dossier et les objets liés au référencement du dossier, vous devez le traiter comme s'il s'agissait d'une collection de fichiers:

$ cp -r folder1/ folder2
$ rm -rf folder1
mbb
la source
3

La conséquence de la modification de la valeur par défaut serait que des milliers de scripts shell se briseraient. Cela conduit aux exigences POSIX et SUS pour le comportement par défaut bien connu.

La raison en est le développement historique des commandes cp, ln et mv (le même binaire sur la plupart des anciens systèmes UNIX) dans diverses branches UNIX. Lorsqu’elle -rest apparue (à l’époque, elle cpn’avait pas la possibilité de copier des répertoires; voici une page de manuel de cp sans -rou -R), il existait diverses différences dans la gestion des fichiers spéciaux, des liens symboliques et autres aléas du système de fichiers.

D'après les spécifications de base du groupe ouvert, numéro 7 :

Les versions antérieures de cette norme incluaient la prise en charge de l'option -r pour la copie des hiérarchies de fichiers. L'option -r est une pratique historique sur les systèmes BSD et dérivés de BSD. Cette option n'est plus spécifiée par POSIX.1-2008 mais peut être présente dans certaines implémentations. L'option -R a été ajoutée en tant que synonyme proche de l'option -r, sélectionnée pour la cohérence avec toutes les autres options de ce volume de POSIX.1-2008 effectuant une descente de répertoire récursive.

La différence entre -R et l'option -r supprimée réside dans le traitement par cp de types de fichiers autres que normal et directory. Elle a été définie par la manière dont l'option traitait les fichiers spéciaux pour permettre à la fois les implémentations historiques et celles qui choisissaient de prendre en charge -r avec les mêmes capacités que -R défini par ce volume de POSIX.1-2008. L'option -r d'origine, pour des raisons historiques, ne traitait pas les fichiers spéciaux différemment des fichiers normaux, mais lisait toujours le fichier et en copiait le contenu. Cela posait des problèmes évidents en présence de types de fichiers spéciaux; par exemple, les périphériques de caractères, les FIFO et les sockets.

En fait, vous verrez toujours des personnes qui utilisent régulièrement:

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

Parce qu'ils ne croient pas que l' cp -rimplémentation sera ce à quoi ils sont habitués sur une machine arbitraire; Ou parce qu'ils veulent le tarcomportement.

Kael
la source
3

Il s’agit peut-être d’une interface sous-optimale aujourd’hui, mais c’est une décision qui a été prise vers 1970 lors de la conception d’UNIX, alors que le disque coûtait considérablement plus cher. Des millions de scripts shell en dépendent, et il est bien trop tard pour le changer.

Voir cet article pour les informations de conception d'origine.

pjc50
la source
3

Un avantage évident de l' -rindicateur est que vous pouvez cp * /target/dircopier uniquement tous les fichiers du répertoire source dans le répertoire cible, en omettant (bien qu'avec un avertissement) tous les répertoires qu'il contient. cp -r * /target/dircopierait tout à la place, y compris les sous-répertoires.

Interneci
la source
2

Vous avez besoin de cet indicateur uniquement lorsqu'il cps'agit d'une commande de copie de fichiers et de répertoires, et pas uniquement de répertoires.

S'il existait une commande spéciale pour la copie de répertoires, le comportement «par défaut» serait sûrement une copie récursive.

mec
la source
1
Logique. Mais pourquoi quelqu'un copierait-il un répertoire qui ne contient pas au moins un fichier? Pourquoi ne pas alors simplement utiliser mkdir?
JakeGould
1
@JakeGould peut-être parce qu'ils peuvent avoir besoin de préserver la propriété et les autorisations?
Ruslan
1

Comme d'autres l'ont mentionné, un répertoire est fondamentalement juste un autre type de fichier (par opposition à un fichier normal), qui "contient" généralement (pointe vers) d'autres fichiers. Il pourrait contenir des sous-répertoires, pour lesquels la même chose s'applique ...

Donc, si vous copiez un répertoire (perspective de l'utilisateur), vous copiez en réalité un groupe de fichiers (perspective du système de fichiers) (fichiers normaux, fichiers de répertoires, liens symboliques, ...) et pour chaque fichier de répertoire, vous répétez cela de manière récursive. processus. Comme la copie d'un répertoire est par définition un processus récursif, l'argument de cp est appelé --recursive.

Il est bien entendu très facile de créer un raccourci de commande dans votre environnement utilisateur (placez-le dans votre fichier .profile / .bashrc pour le rendre disponible en permanence):

alias cpr='cp -r'

Ou peut-être mieux:

alias cpa='cp -av'

De cette façon, vous pouvez copier un répertoire en utilisant cpa dir1 copyDir1et il imprimera non seulement ce qui est copié, mais appliquera également des autorisations de fichier.

Et comme quelqu'un a mentionné que cp pourrait théoriquement détecter que le fichier source est un répertoire et demander s'il doit le copier de manière récursive, voici une suggestion rapide:

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 "$@"
}

Ceci est juste un wrapper cp pas cher. Il conserve toujours toutes les métadonnées (c.-à-d. Copie l'heure de modification du fichier, copie correctement les liens symboliques, etc.) et si vous essayez de copier un répertoire, il vous demande s'il doit le copier (de manière récursive).

basic6
la source