Perché -r ricorsivo è necessario quando si copia una directory in Linux?


47

La mia domanda è: perché è necessario usare il -rflag (ricorsivo) quando si fa una copia di una directory? Cioè, perché farlo:

$ cp -r dir1 copyDir1

Quando non vorrei questo comportamento durante la copia di una directory?

Una copia ricorsiva di una directory non è davvero il comportamento "predefinito"; il comportamento che vogliamo quasi sempre?

Sembra che questa sia una bandiera superflua.


Non avresti bisogno di copiare anche i file e le cartelle in esso?
QuyNguyen2013,

Se ritieni che questo sarebbe un miglioramento, puoi ripubblicare questa richiesta sul canale degli sviluppatori. Altrimenti è stato probabilmente programmato molto tempo fa.
blogger,

@blogger È stato programmato molto tempo fa, ma per un motivo. Ciò significa che se qualcuno vuole svolgere un lavoro di base in un ambiente a riga di comando, il suo compito dovrebbe essere semplice come evitare un errore del sistema. Ciò significa che esistono buone ragioni per cui esistono convenzioni di interazione con l'utente dalla riga di comando. Espando questo concetto nella mia risposta.
Jake Gould,

2
Questa domanda è stato chiesto e ha risposto sul unix.SE .
dotancohen,

Lo stesso vale perrm

Risposte:


58

Nel modo in cui funzionano i filesystem, una directory non è in realtà una cartella contenente file, ma piuttosto una directory è un file che contiene puntatori inode a file "figli" ad essa collegati. Significato, dal punto di vista del file system, un file è un file, ma una directory è solo un file contenente un elenco di file collegati.

Quindi dal punto di vista della riga di comando, facendo questo:

$ cp dir1 copyDir1

Significherebbe sostanzialmente copiare il file denominato, dir1in un nuovo file denominato copyDir1. E per quanto riguarda il file system, dir1è comunque solo un file; il fatto che si tratti di una "directory" sarà evidente solo quando il filesystem verifica effettivamente dir1che cosa sia effettivamente quel mucchio di bit.

Il -rflag indica al file system di scorrere in modo ricorsivo l'albero dei file / directory e di copiare tutti i contenuti che potrebbero essere "figli" di quel file in una nuova posizione.

Ora sul perché ciò possa sembrare superfluo o ridondante, ciò si riduce davvero ai metodi storici di gestione dei file system. Oltre a creare un sistema sicuro da tutti i tipi di errori relativi all'utente; accidentale e intenzionale.

Significato, supponiamo che tu abbia un ~/binfile nella tua home directory che vuoi copiare ma accidentalmente escluso il ~—perché sei un essere umano e commetti errori — quindi è proprio /bincosì:

cp /bin/ ~/copy_of_bin

Con la "rete di sicurezza" di /binessere una directory unita alla necessità del -rflag, eviterai di copiare accidentalmente l'intera radice binaria del sistema su cui ti trovi nella tua home directory. Se quella rete di sicurezza non esistesse, si verificherebbe un disastro minore, o forse maggiore.

La logica qui è che ai giorni devono essere impostate convenzioni logiche / comportamentali pre-GUI (interfacce utente grafiche) per evitare che l'utente abbia creato incidenti che possono potenzialmente uccidere un sistema. E l'uso della -rbandiera ora è uno di questi.

Se questo sembra superfluo, non è necessario cercare oltre il moderno sistema di interfaccia grafica che si può collocare sopra i file system Linux. Una GUI risolve i problemi di base dell'utente come questo consentendo di trascinare e rilasciare file e directory con facilità.

Ma nel caso delle interfacce testuali, gran parte dell '"esperienza utente" all'interno di quel mondo è fondamentalmente solo dossi stradali logici e basati su hueristic che aiutano a tenere sotto controllo l'utente in modo da evitare il potenziale disastro.

Allo stesso modo, questo è il motivo per cui i filesystem Linux / Unix non hanno 777permessi e sudodiritti impostati di default e come gli amministratori di sistema reali sussultano quando un utente imposta 777permessi o concede sudodiritti a tutti . Queste sono le cose basilari che si fanno per garantire che il sistema sia stabile e il più “a prova dell'utente” possibile; chiunque si affretti a cortocircuitare quelle convenzioni molto probabilmente causerà danni al proprio sistema senza nemmeno saperlo.

INFORMAZIONI AGGIUNTIVE: Un'altra risposta qui sul sito Unix Stack Exchange fornisce una buona spiegazione del perché una copia non ricorsiva di una directory è problematica; l'enfasi è mia.

Bene, senza il flag -R, è possibile solo copiare i file, perché è piuttosto insolito che qualcuno voglia copiare una directory in modo non ricorsivo : una copia non ricorsiva comporterebbe solo un secondo nome per la directory, che punta direttamente al stessa struttura di directory. Dato che raramente è ciò che la gente vuole, e in realtà esiste un programma separato che lo fa (ln), non è consentita una copia non ricorsiva di directory.

Quindi se una directory è solo un file con elementi inode al suo interno, fare una copia diretta di quel file sarebbe solo l'equivalente di come funzionerebbe un hard link. Che non è quello che qualcuno vuole.


19
Personalmente penso che l'aspetto "protezione" non superi il test dell'olfatto. Alcuni umani potrebbero digitare cp -r /binaltrettanto facilmente cp-r ~/bin. La bandiera stessa non impedisce errori o rende necessariamente migliore qualcuno a stare attenti. Se si desidera prevenire errori, il comando cp potrebbe altrettanto facilmente guardare il nodo in questione e fornire un prompt, qualcosa sulla falsariga di "Questa è una directory, vuoi copiare tutto il contenuto nella posizione specificata (y / n)?" Questo sarebbe rete di sicurezza . Richiedere -r per le directory mantiene il gonfiore di codice più che altro.
JDL,

12
Cattiva analogia con il tombino. Come diceva @JDL, la bandiera in questione non fa nulla per impedire un errore di battitura nel percorso. Sarei più felice di accettare la coerenza con altri comandi come motivo, ma ho la sensazione che il vero motivo sia "è così che è stato scritto per la prima volta e ora così tante cose si basano su quel comportamento, modificarlo è impossibile".
Basic

7
Quando spostiamo una directory non abbiamo bisogno di -r. Penso che la risposta collegata a unix.stackexchange.com sia molto più pertinente. L'equivalente di una copia non ricorsiva sarebbe avere una seconda directory e collegamenti reali per tutti i file all'interno della struttura di directory.
Gerrit,

2
Se non sbaglio, -r era un'estensione GNU - non credo che lo storico cp UNIX avesse una copia ricorsiva - e questo faceva parte del motivo del comando rsync .
Mei,

2
@JakeGould Capisco concetti di base. Tuttavia, sostengo contro l'idea che un flag -r sul comando in questione fornisca ulteriore sicurezza. Dalla mia esperienza, i comandi di Linux da riga di comando sono storicamente intrinsecamente non sicuri. La sicurezza è stata aggiunta agli straordinari, se non del tutto. La sicurezza insita nella progettazione di Linux proviene dal sistema dei permessi e continua a funzionare come root. Non funzionare come root è anche qualcosa che è più una convenzione che un design. La convenzione è supportata nella maggior parte dei nuovi programmi di installazione di Linux, ma non sempre.
JDL

19

È vero che questo è il comportamento che vogliamo quasi sempre. Questo non significa necessariamente, tuttavia, che la copia ricorsiva dovrebbe essere il comportamento predefinito.

Penso che le ragioni cpagiscano in quanto hanno radici nella filosofia Unix . Unix privilegia i programmi che fanno una cosa e la fanno bene , così come i programmi che sono semplici sia nell'interfaccia che nell'implementazione (a volte chiamato peggio è meglio ).

Il pezzo chiave del puzzle qui è rendersi conto che cpnon copia le directory - cpcopia i file (e solo i file). Se si desidera copiare una directory, si cp chiama in modo ricorsivo , al fine di copiare i file su ciascuna directory.

Naturalmente, la differenza tra "copiare le directory" e "copiare i file in modo ricorsivo" non è assolutamente nulla, dal punto di vista dell'utente, ma avere questa interfaccia aiuta l'implementazione a rimanere semplice .

Se hai reso in cpgrado di copiare le directory, verrai presto tentato di aggiungere altre funzionalità che hanno senso solo per le directory, ad esempio potresti voler copiare solo i nomi dei file finiti .sh. Inevitabilmente, questo porta al gonfiore e al creep a cui siamo abituati in altri sistemi operativi, rendendo il software lento, complesso e soggetto a errori.

Un altro vantaggio è che avere -raiuta anche l'utente a capire cosa sta realmente accadendo sotto l'interfaccia. Un piacevole effetto collaterale di questo è che apprendere il concetto di operazione ricorsiva ti risparmierà un po 'di lavoro quando apprendi altri strumenti che lo supportano (come grep, ad esempio)


Alcune persone ti diranno sicuramente che esporre i dettagli dell'implementazione all'utente è negativo e che avere più funzionalità è buono . Il mio intento qui è semplicemente spiegare la logica di questo comportamento, quindi non proverò a discutere in entrambi i modi.


2
+1 "... fai una cosa e fallo bene ..." Grazie per averlo dichiarato!
Jake Gould

5

Le interazioni con le directory ti assicurano che stai interagendo con una directory e NON con un solo file.

Per esempio:

$ 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

Quindi, se si desidera copiare correttamente una cartella, poiché implica sia la cartella che gli oggetti relativi al riferimento alla cartella, è necessario trattarla come se fosse una raccolta di file:

$ cp -r folder1/ folder2
$ rm -rf folder1

3

La conseguenza della modifica del valore predefinito sarebbe la rottura di migliaia di script di shell. Ciò porta ai requisiti POSIX e SUS per il noto comportamento predefinito.

Il motivo è lo sviluppo storico dei comandi cp, ln e mv (tutti uguali binari sulla maggior parte dei vecchi sistemi UNIX) in vari rami UNIX. Quando è -rapparso (early cpnon aveva un'opzione per copiare le directory; ecco una prima pagina man di cp senza -ro -R), c'erano diverse differenze nella gestione di file speciali, collegamenti simbolici e altri capricci del filesystem.

Dalle specifiche Open Base Base Numero 7 :

Le versioni precedenti di questo standard includevano il supporto per l'opzione -r per copiare le gerarchie di file. L'opzione -r è una pratica storica sui sistemi derivati ​​da BSD e BSD. Questa opzione non è più specificata da POSIX.1-2008 ma potrebbe essere presente in alcune implementazioni. L'opzione -R è stata aggiunta come un sinonimo stretto dell'opzione -r, selezionata per coerenza con tutte le altre opzioni in questo volume di POSIX.1-2008 che eseguono la discesa ricorsiva della directory.

La differenza tra -R e l'opzione -r rimossa è nel trattamento di cp di tipi di file diversi da quelli regolari e directory. È stato definito dall'implementazione il modo in cui l'opzione - ha trattato file speciali per consentire sia implementazioni storiche che quelle che hanno scelto di supportare -r con le stesse capacità di -R definite da questo volume di POSIX.1-2008. Il flag originale -r, per motivi storici, non gestiva file speciali in modo diverso dai file normali, ma leggeva sempre il file e ne copiava il contenuto. Ciò ha avuto ovvi problemi in presenza di tipi di file speciali; ad esempio, dispositivi a caratteri, FIFO e socket.

In effetti vedrai ancora alcune persone che usano regolarmente:

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

Perché non si fidano che l' cp -rimplementazione sia quella a cui sono abituati su una macchina arbitraria; O perché vogliono il tarcomportamento.


3

Potrebbe essere un'interfaccia utente non ottimale oggi, ma è stata una decisione presa circa 1970 durante la progettazione di UNIX quando il disco era considerevolmente più costoso. Milioni di script di shell dipendono dal fatto che funzioni in questo modo, ed è troppo tardi per cambiarlo.

Vedi questo articolo per le informazioni sul design originale.


3

Un chiaro vantaggio del -rflag è che è possibile cp * /target/dircopiare solo tutti i file nella directory di origine nella directory di destinazione, omettendo (sebbene con un avviso) tutte le directory in essa contenute. cp -r * /target/dirinvece copia tutto, comprese le sottodirectory.


2

È necessario questo flag solo quando cpè un comando per copiare file e directory e non solo directory.

Se esistesse un comando speciale per la copia delle directory, il comportamento "predefinito" sarebbe sicuramente una copia ricorsiva.


1
Ha senso. Ma perché qualcuno dovrebbe copiare una directory che non contiene almeno un file? Perché non usare semplicemente mkdir?
Jake Gould,

1
@JakeGould forse perché potrebbe essere necessario preservare la proprietà e le autorizzazioni?
Ruslan,

1

Come altri hanno già detto, una directory è sostanzialmente solo un altro tipo di file (al contrario di un normale file), che di solito "contiene" (punta a) altri file. Potrebbe contenere sottodirectory, per le quali vale lo stesso ...

Quindi, se stai copiando una directory (prospettiva dell'utente), stai davvero copiando un mucchio di file (prospettiva del filesystem) (file normali, file di directory, collegamenti simbolici, ...) e per ogni file di directory, stai ripetendo in modo ricorsivo che processi. Poiché la copia di una directory è per definizione un processo ricorsivo, viene chiamato l'argomento di cp --recursive.

Naturalmente, è molto semplice creare un collegamento al comando nel proprio ambiente utente (inserirlo nel file .profile / .bashrc per renderlo permanentemente disponibile):

alias cpr='cp -r'

O forse meglio:

alias cpa='cp -av'

In questo modo, puoi copiare una directory usando cpa dir1 copyDir1e non solo stamperà ciò che viene copiato, ma applicherà anche le autorizzazioni per i file.

E poiché qualcuno ha affermato che cp potrebbe teoricamente rilevare che il file sorgente è una directory e chiedere se deve copiarlo in modo ricorsivo, ecco un breve suggerimento:

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

Questo è solo un wrapper cp economico. Conserva sempre tutti i metadati (ad esempio, copia il tempo di modifica del file, copia correttamente i collegamenti simbolici e così via) e se stai cercando di copiare una directory, ti chiede se deve copiarlo (ricorsivamente).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.