Mengapa -r rekursif diperlukan saat menyalin direktori di Linux?

47

Pertanyaan saya adalah mengapa harus menggunakan -rflag (rekursif) ketika membuat salinan direktori? Yaitu, mengapa melakukan ini:

$ cp -r dir1 copyDir1

Kapan saya tidak ingin perilaku ini saat menyalin direktori?

Bukan salinan rekursif direktori yang benar-benar perilaku "default"; perilaku yang kita inginkan hampir setiap saat?

Rasanya seperti ini adalah bendera yang berlebihan.

Madeleine P. Vincent
sumber
Tidakkah Anda perlu menyalin file dan folder di dalamnya juga?
QuyNguyen2013
Jika menurut Anda ini merupakan peningkatan, Anda dapat memposting ulang permintaan ini di saluran pengembang. Kalau tidak, itu mungkin sudah diprogram sejak lama.
blogger
@blogger Sudah diprogram sejak lama, tetapi karena suatu alasan. Berarti jika seseorang ingin melakukan pekerjaan dasar di lingkungan baris perintah, tugas mereka seharusnya semudah menghindari kegagalan sistem itu sulit. Berarti ada alasan bagus beberapa konvensi interaksi pengguna baris perintah ada. Saya mengembangkan konsep ini dalam jawaban saya.
JakeGould
2
Pertanyaan ini telah diajukan dan dijawab pada unix.SE .
dotancohen
Hal yang sama berlaku untukrm

Jawaban:

58

Cara kerja filesystem, direktori sebenarnya bukan folder yang berisi file tetapi direktori adalah file yang berisi inode pointer ke file “child” yang terhubung dengannya. Artinya, dari perspektif sistem file, file adalah file, tetapi direktori hanyalah file yang berisi daftar file yang terhubung.

Jadi dari perspektif baris perintah, lakukan ini:

$ cp dir1 copyDir1

Pada dasarnya berarti menyalin nama file, dir1ke file baru bernama copyDir1. Dan sejauh menyangkut sistem file, dir1tetap saja file; sebenarnya itu adalah "direktori" hanya akan terlihat ketika filesystem benar-benar memeriksa dir1untuk melihat apa tumpukan bit sebenarnya.

The -rflag mengatakan sistem file untuk gulungan rekursif bawah file / pohon direktori dan menyalin setiap & semua isi yang mungkin menjadi “anak” dari file tersebut ke tempat baru.

Sekarang mengapa itu mungkin tampak berlebihan atau berlebihan, ini benar-benar datang ke metode historis berurusan dengan sistem file. Serta menciptakan sistem yang aman dari semua jenis kesalahan terkait pengguna; disengaja serta disengaja.

Artinya, katakanlah Anda memiliki ~/binfile di direktori home yang ingin Anda salin tetapi tidak sengaja meninggalkan ~— karena Anda adalah manusia dan melakukan kesalahan — jadi /binseperti ini:

cp /bin/ ~/copy_of_bin

Dengan "jaring pengaman" /binsebagai direktori yang dipadukan dengan kebutuhan akan -rflag, Anda akan terhindar dari menyalin secara tidak sengaja seluruh akar biner dari sistem yang Anda gunakan pada direktori home Anda. Jika jaring pengaman itu tidak ada, bencana kecil - atau mungkin besar - akan terjadi.

Logikanya di sini adalah bahwa pada hari-hari pra-GUI (antarmuka pengguna grafis) konvensi logis / perilaku perlu diatur untuk menghindari pengguna membuat kecelakaan yang berpotensi membunuh sistem. Dan menggunakan -rbendera sekarang adalah salah satunya.

Jika itu tampak berlebihan, maka tidak perlu melihat lebih jauh dari sistem GUI modern yang dapat ditempatkan di atas sistem file Linux. GUI mengatasi masalah pengguna dasar seperti ini dengan memungkinkan orang menyeret dan menjatuhkan file dan direktori dengan mudah.

Tetapi dalam kasus bidang antarmuka berbasis teks, banyak "pengalaman pengguna" dalam dunia itu pada dasarnya hanya benjolan jalan yang logis dan berbasis hueristik yang membantu menjaga pengguna agar tetap terkendali sehingga potensi bencana dapat dihindari.

Demikian pula ini sebabnya sistem file Linux / Unix tidak memiliki 777izin dan sudohak yang ditetapkan secara default dan bagaimana administrator sistem nyata meringis ketika pengguna menetapkan 777izin atau memberikan sudohak kepada semua orang . Ini adalah hal-hal dasar yang dilakukan seseorang untuk memastikan sistem stabil dan "bukti pengguna" mungkin; siapa pun yang terburu-buru untuk memutus konvensi tersebut kemungkinan besar akan menyebabkan kerusakan pada sistem mereka tanpa menyadarinya.

INFO TAMBAHAN: Jawaban lain di sini di situs Unix Stack Exchange memberikan penjelasan yang baik tentang mengapa salinan direktori yang tidak rekursif bermasalah; penekanan adalah milikku.

Nah, tanpa tanda -R, itu hanya mungkin untuk menyalin file, karena itu agak tidak biasa bahwa seseorang ingin menyalin secara non-rekursif direktori: Salinan non-rekursif hanya akan menghasilkan nama kedua untuk direktori, menunjuk langsung ke struktur direktori yang sama. Karena itu jarang yang diinginkan orang, dan sebenarnya ada program terpisah yang melakukan ini (ln), salinan direktori non-rekursif tidak diperbolehkan.

Jadi, jika sebuah direktori hanyalah sebuah file dengan item inode di dalamnya, membuat salinan langsung dari file itu hanya akan setara dengan cara kerja tautan keras. Yang tidak diinginkan siapa pun.

JakeGould
sumber
19
Saya pribadi berpikir aspek "perlindungan" tidak lulus uji bau. Beberapa manusia bisa dengan mudah mengetik cp -r /binsebagai cp-r ~/bin. Bendera itu sendiri tidak mencegah kesalahan atau membuat orang lebih baik dalam berhati-hati. Jika Anda ingin mencegah kesalahan, perintah cp dapat dengan mudah melihat node yang dimaksud dan memberikan prompt, sesuatu di sepanjang baris "Ini adalah direktori, apakah Anda ingin menyalin semua konten ke lokasi yang ditentukan (y / n)? " Itu akan menjadi jaring pengaman . Memerlukan -r untuk direktori menjaga kode tetap turun lebih dari apa pun.
JDL
12
Analogi buruk dengan manhole. Seperti yang dikatakan @JDL, flag yang dimaksud tidak melakukan apa pun untuk mencegah kesalahan ketik di path. Saya akan lebih senang menerima konsistensi dengan perintah lain sebagai alasan, tapi saya punya perasaan alasan sebenarnya adalah "begitulah pertama kali ditulis dan sekarang banyak hal bergantung pada perilaku itu, mengubah itu tidak mungkin".
Dasar
7
Ketika kita memindahkan direktori, kita tidak perlu -r. Saya pikir jawaban yang tertaut di unix.stackexchange.com jauh lebih penting. Setara dengan salinan non-rekursif akan memiliki direktori kedua, dan tautan keras untuk semua file di dalam pohon direktori.
gerrit
2
Jika saya tidak salah, -r adalah ekstensi GNU - Saya tidak berpikir UNIX cp memiliki salinan rekursif - dan itu adalah bagian dari alasan untuk perintah rsync .
Mei
2
@JakeGould saya mengerti konsep dasar. Saya, bagaimanapun, menentang gagasan bahwa bendera -r pada perintah tersebut memberikan keamanan tambahan. Dari pengalaman saya, perintah perintah linux secara historis tidak aman. Keamanan telah ditambahkan dari waktu ke waktu, jika ada. Keamanan yang melekat dalam desain linux berasal dari sistem izin dan tidak berjalan sebagai root. Tidak berjalan sebagai root juga sesuatu yang lebih dari sebuah konvensi daripada desain. Konvensi ini didukung di sebagian besar penginstal linux baru, tetapi tidak selalu.
JDL
19

Sangat benar bahwa ini adalah perilaku yang kita inginkan hampir setiap saat. Namun, ini tidak berarti bahwa menyalin secara rekursif harus menjadi perilaku default.

Saya pikir alasannya cpbertindak karena memang berakar pada filosofi Unix . Unix menyukai program yang melakukan satu hal dan melakukannya dengan baik , serta program yang sederhana di antarmuka dan implementasi (kadang-kadang disebut lebih buruk lebih baik ).

Bagian penting dari teka-teki di sini adalah menyadari bahwa cptidak menyalin direktori - cpmenyalin file (dan hanya file). Jika Anda ingin menyalin direktori, cp panggil dirinya sendiri secara rekursif , untuk menyalin file pada setiap direktori.

Tentu saja, perbedaan antara "menyalin direktori" dan "menyalin file secara rekursif" sama sekali tidak ada, dari perspektif pengguna, tetapi memiliki antarmuka ini membantu implementasinya tetap sederhana .

Jika Anda berhasil cpmenyalin direktori, Anda akan segera tergoda untuk menambahkan lebih banyak fitur yang hanya masuk akal untuk direktori - misalnya, Anda mungkin ingin hanya menyalin nama file yang diakhiri dengan .sh. Tidak dapat dihindari, ini mengarah pada bloat dan fitur creep yang biasa kita gunakan dalam sistem operasi lain - membuat perangkat lunak lambat, rumit dan rawan kesalahan.

Keuntungan lain adalah memiliki -rjuga membantu pengguna memahami apa yang sebenarnya terjadi di bawah antarmuka. Efek samping yang bagus dari ini adalah bahwa mempelajari konsep operasi rekursif akan membantu Anda ketika Anda belajar tentang alat lain yang mendukungnya (seperti grep, misalnya)


Beberapa orang pasti akan memberi tahu Anda bahwa mengekspos detail implementasi kepada pengguna itu buruk , dan memiliki lebih banyak fitur bagus . Maksud saya di sini hanyalah menjelaskan alasan perilaku ini, jadi saya tidak akan mencoba berdebat dengan cara apa pun.

goncalopp
sumber
2
+1 "... lakukan satu hal dan lakukan dengan baik ..." Terima kasih telah menyatakan ini!
JakeGould
5

Interaksi dengan direktori memastikan Anda tahu Anda berinteraksi dengan direktori dan BUKAN hanya satu file.

Misalnya:

$ 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

Jadi, jika Anda ingin berhasil menyalin folder, karena itu menyiratkan folder dan objek yang terkait dengan referensi folder, Anda harus memperlakukannya seolah-olah itu kumpulan file:

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

Konsekuensi dari mengubah default adalah bahwa ribuan script shell akan rusak. Ini mengarah pada persyaratan POSIX dan SUS untuk perilaku default yang sudah dikenal luas.

Alasannya adalah perkembangan historis perintah cp, ln dan mv (semua biner yang sama pada kebanyakan sistem UNIX lama) di berbagai cabang UNIX. Ketika -rmuncul (awal cptidak memiliki opsi untuk menyalin direktori; di sini adalah halaman manual cp awal tanpa -ratau -R), ada berbagai perbedaan dalam menangani file khusus, symlink dan dan keanehan lain dari sistem file.

Dari Spesifikasi Basis Grup Terbuka, Edisi 7 :

Versi sebelumnya dari standar ini termasuk dukungan untuk opsi -r untuk menyalin hierarki file. Opsi -r adalah praktik historis pada sistem yang diturunkan BSD dan BSD. Opsi ini tidak lagi ditentukan oleh POSIX.1-2008 tetapi dapat hadir dalam beberapa implementasi. Opsi -R ditambahkan sebagai sinonim dekat dengan opsi -r, dipilih untuk konsistensi dengan semua opsi lain dalam volume POSIX.1-2008 ini yang melakukan turunan direktori rekursif.

Perbedaan antara opsi -R dan opsi -r yang dihapus ada dalam perawatan dengan cp tipe file selain dari direktori biasa dan. Itu adalah implementasi yang ditentukan bagaimana opsi - memperlakukan file khusus untuk memungkinkan implementasi historis dan yang memilih untuk mendukung -r dengan kemampuan yang sama seperti -R yang didefinisikan oleh volume POSIX.1-2008 ini. Bendera -r asli, karena alasan historis, tidak menangani file khusus secara berbeda dari file biasa, tetapi selalu membaca file dan menyalin isinya. Ini memiliki masalah yang jelas di hadapan jenis file khusus; misalnya, perangkat karakter, FIFO, dan soket.

Bahkan Anda masih akan melihat beberapa orang secara teratur menggunakan:

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

Karena mereka tidak percaya bahwa cp -rimplementasi dengan apa yang mereka gunakan pada mesin sewenang-wenang; Atau karena mereka menginginkan tarperilakunya.

kael
sumber
3

Ini mungkin UI suboptimal hari ini, tapi itu keputusan yang diambil sekitar tahun 1970 selama desain UNIX ketika disk jauh lebih mahal. Jutaan skrip shell bergantung padanya untuk bekerja seperti itu, dan sudah terlambat untuk mengubahnya.

Lihat artikel ini untuk info desain aslinya.

pjc50
sumber
3

Keuntungan yang jelas dari -rflag adalah bahwa Anda dapat cp * /target/dirmenyalin hanya semua file di direktori sumber ke direktori target, menghilangkan (meskipun dengan peringatan) semua direktori yang terkandung di dalamnya. cp -r * /target/dirakan menyalin semuanya sebagai gantinya, termasuk subdirektori.

Interneci
sumber
2

Anda memerlukan flag ini hanya ketika itu cpadalah perintah untuk menyalin file dan direktori dan bukan hanya direktori.

Jika ada perintah khusus untuk salinan direktori, perilaku "default" pasti akan menjadi salinan rekursif.

duDE
sumber
1
Masuk akal. Tetapi mengapa seseorang menyalin direktori yang tidak memiliki setidaknya satu file di dalamnya? Mengapa tidak menggunakan saja mkdir?
JakeGould
1
@JakeGould mungkin karena mereka mungkin perlu mempertahankan kepemilikan dan izin?
Ruslan
1

Seperti yang disebutkan orang lain, direktori pada dasarnya hanyalah tipe file lain (berbeda dengan file biasa), yang biasanya "berisi" (menunjuk ke) file-file lain. Ini bisa berisi subdirektori, yang sama berlaku ...

Jadi jika Anda menyalin direktori (perspektif pengguna), Anda benar-benar menyalin banyak file (perspektif filesystem) (file biasa, file direktori, tautan simbolis, ...) dan untuk setiap file direktori, Anda mengulangi secara berulang bahwa proses. Karena menyalin direktori secara definisi merupakan proses rekursif, argumen cp disebut --recursive.

Tentu saja sangat mudah untuk membuat pintasan perintah di lingkungan pengguna Anda (letakkan ini di file .profile / .bashrc Anda agar tersedia secara permanen):

alias cpr='cp -r'

Atau mungkin lebih baik:

alias cpa='cp -av'

Dengan begitu, Anda dapat menyalin direktori menggunakan cpa dir1 copyDir1dan itu tidak hanya akan mencetak apa yang sedang disalin tetapi juga menerapkan izin file.

Dan karena seseorang menyebutkan bahwa cp secara teoritis dapat mendeteksi bahwa file sumber adalah direktori dan bertanya apakah itu harus menyalinnya secara rekursif, berikut saran singkatnya:

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

Ini hanya pembungkus cp murah. Itu selalu mempertahankan semua data meta (yaitu, menyalin waktu modifikasi file, benar menyalin symlinks dan sebagainya) dan jika Anda mencoba untuk menyalin direktori, ia bertanya apakah itu harus menyalinnya (secara rekursif).

basic6
sumber