เหตุใดจึงต้องเรียกใช้ -r ซ้ำเมื่อคัดลอกไดเร็กทอรีใน Linux


47

คำถามของฉันคือเหตุใดจึงต้องใช้การ-rตั้งค่าสถานะ (เรียกซ้ำ) เมื่อสร้างสำเนาของไดเรกทอรี คือทำไมทำเช่นนี้:

$ cp -r dir1 copyDir1

ฉันจะไม่ต้องการพฤติกรรมนี้เมื่อคัดลอกไดเรกทอรีหรือไม่?

ไม่ใช่สำเนาที่เรียกซ้ำของไดเรกทอรีว่าเป็นพฤติกรรม "ค่าเริ่มต้น" หรือไม่ พฤติกรรมที่เราต้องการเกือบตลอดเวลา?

รู้สึกเหมือนว่านี่เป็นธงที่ไม่จำเป็น


คุณไม่จำเป็นต้องคัดลอกไฟล์และโฟลเดอร์ด้วยหรือไม่
QuyNguyen2013

หากคุณคิดว่านี่จะเป็นการปรับปรุงคุณสามารถโพสต์คำขอนี้ซ้ำอีกครั้งในช่องของนักพัฒนาซอฟต์แวร์ มิฉะนั้นมันอาจจะถูกตั้งโปรแกรมมานานแล้ว
blogger

@blogger เป็นโปรแกรมมานานแล้ว แต่ด้วยเหตุผล หมายความว่าถ้ามีคนต้องการทำงานขั้นพื้นฐานในสภาพแวดล้อมบรรทัดคำสั่งงานของพวกเขาควรจะง่ายเหมือนการหลีกเลี่ยงความล้มเหลวของระบบเป็นเรื่องยาก ความหมายมีเหตุผลที่ดีมีบางข้อตกลงการโต้ตอบกับผู้ใช้บรรทัดคำสั่งอยู่ ฉันขยายแนวคิดนี้ในคำตอบของฉัน
JakeGould

2
คำถามนี้ได้รับการถามและตอบใน unix.SE
dotancohen

กันไปสำหรับrm

คำตอบ:


58

วิธีการทำงานของระบบไฟล์ไดเรกทอรีไม่ใช่โฟลเดอร์ที่มีไฟล์ แต่เป็นไดเรกทอรีที่เป็นไฟล์ที่มี inode pointersเป็นไฟล์ "child" ที่เชื่อมต่ออยู่ ความหมายจากมุมมองของระบบไฟล์ไฟล์เป็นไฟล์ แต่ไดเร็กทอรีเป็นเพียงไฟล์ที่มีรายการไฟล์ที่เชื่อมต่อ

ดังนั้นจากมุมมองบรรทัดคำสั่งทำสิ่งนี้:

$ cp dir1 copyDir1

โดยทั่วไปจะหมายถึงการคัดลอกไฟล์ชื่อไปยังแฟ้มใหม่ที่ชื่อว่าdir1 copyDir1และเท่าที่ระบบไฟล์มีความกังวลdir1ก็เป็นเพียงไฟล์ต่อไป ความจริงมันเป็น "ไดเรกทอรี" จะปรากฏเฉพาะเมื่อระบบแฟ้มตรวจสอบจริงdir1เพื่อดูว่ากองบิตที่เป็นจริง

-rธงบอกระบบไฟล์ที่จะม้วนลงซ้ำไฟล์ต้น / ไดเรกทอรีและคัดลอกเนื้อหาใด ๆ และทั้งหมดที่อาจจะเป็น“เด็ก” ของไฟล์นั้นไปยังสถานที่ใหม่

ทีนี้สาเหตุที่อาจดูฟุ่มเฟือยหรือซ้ำซ้อนนี่เป็นวิธีการทางประวัติศาสตร์ในการจัดการกับระบบไฟล์ รวมถึงการสร้างระบบที่ปลอดภัยจากข้อผิดพลาดที่เกี่ยวข้องกับผู้ใช้ทุกประเภท โดยไม่ตั้งใจเช่นเดียวกับความตั้งใจ

หมายความว่าคุณมี~/binไฟล์ในโฮมไดเร็กตอรี่ของคุณที่คุณต้องการคัดลอก แต่ไม่ได้ตั้งใจ~เพราะคุณเป็นมนุษย์และทำผิดพลาด - ดังนั้นมันจึงเป็น/binเช่นนี้:

cp /bin/ ~/copy_of_bin

ด้วย "ความปลอดภัยสุทธิ" ของ/binการเป็นไดเรกทอรีรวมกับความต้องการสำหรับการ-rตั้งค่าสถานะคุณจะหลีกเลี่ยงการคัดลอกทั้งรากไบนารีของระบบที่คุณอยู่ในไดเรกทอรีบ้านของคุณโดยไม่ตั้งใจ หากไม่มีเครือข่ายความปลอดภัยนั้นผู้เยาว์ - หรือที่สำคัญอาจเป็นหายนะก็จะเกิดขึ้น

ตรรกะในที่นี้คือในสมัยก่อน GUI (ส่วนต่อประสานผู้ใช้แบบกราฟิก) การประชุมแบบลอจิคัล / พฤติกรรมจะต้องมีการตั้งค่าเพื่อหลีกเลี่ยงการมีผู้ใช้สร้างอุบัติเหตุที่อาจฆ่าระบบ และการใช้-rธงเป็นหนึ่งในนั้น

หากดูเหมือนว่าฟุ่มเฟือยแล้วไม่ต้องมองไกลไปกว่าระบบ GUI ที่ทันสมัยอย่างใดอย่างหนึ่งสามารถวางเหนือระบบไฟล์ Linux GUI จัดการปัญหาพื้นฐานของผู้ใช้เช่นนี้โดยอนุญาตให้ลากและวางไฟล์และไดเรกทอรีได้อย่างง่ายดาย

แต่ในกรณีของขอบเขตของอินเตอร์เฟซที่ใช้ข้อความเป็นจำนวนมาก“ ประสบการณ์ผู้ใช้” ในโลกนั้นเป็นเพียงแค่การกระแทกบนถนนแบบตรรกะและแบบใช้ความคิดสร้างสรรค์ซึ่งช่วยให้ผู้ใช้อยู่ในการตรวจสอบเพื่อป้องกันภัยพิบัติที่อาจเกิดขึ้น

ในทำนองเดียวกันนี้คือเหตุผลที่ filesystems Linux / Unix ไม่ได้มี777สิทธิ์และsudoสิทธิที่กำหนดโดยค่าเริ่มต้นและวิธีการที่ระบบจริงผู้บริหารสะดุ้งเมื่อมีการตั้งค่าผู้ใช้777สิทธิ์หรือทุนทุกคนsudoสิทธิ สิ่งเหล่านี้เป็นสิ่งพื้นฐานที่เราทำเพื่อให้แน่ใจว่าระบบมีเสถียรภาพและเป็น "หลักฐานการใช้งาน" ที่สุด ใครก็ตามที่วิ่งไปลัดวงจรการประชุมเหล่านั้นมักจะทำให้ระบบของพวกเขาเกิดความเสียหายโดยที่ไม่รู้ตัว

ข้อมูลเพิ่มเติม: คำตอบอื่นที่นี่ในเว็บไซต์ Unix Stack Exchangeให้คำอธิบายที่ดีว่าเหตุใดสำเนาที่ไม่ใช่แบบเรียกซ้ำของไดเรกทอรีจึงเป็นปัญหา เน้นเป็นของฉัน

ถ้าไม่มีแฟล็ก -R มันเป็นไปได้ที่จะคัดลอกไฟล์เท่านั้นเพราะมันค่อนข้างแปลกที่บางคนต้องการที่จะไม่ทำการคัดลอกไดเรกทอรีซ้ำ: สำเนาที่ไม่ใช่แบบเรียกซ้ำจะทำให้ชื่อที่สองของไดเรกทอรีชี้ไปที่ โครงสร้างไดเรกทอรีเดียวกัน เพราะนั่นเป็นสิ่งที่คนต้องการและจริงๆแล้วมีโปรแกรมแยกต่างหากที่ทำเช่นนี้ (ln) จึงไม่อนุญาตให้คัดลอกไดเรกทอรีที่ไม่เกิดซ้ำ

ดังนั้นหากไดเรกทอรีเป็นเพียงไฟล์ที่มีไอโหนดในนั้นการทำสำเนาไฟล์ตรงนั้นจะเท่ากับการทำงานของฮาร์ดลิงก์ ซึ่งไม่ใช่สิ่งที่ทุกคนต้องการ


19
โดยส่วนตัวแล้วฉันคิดว่า "การป้องกัน" ไม่สามารถผ่านการทดสอบกลิ่นได้ มนุษย์บางคนอาจได้อย่างง่ายดายเพียงพิมพ์เป็นcp -r /bin cp-r ~/binการตั้งค่าสถานะนั้นไม่ได้ป้องกันความผิดพลาดหรือทำให้ผู้อื่นระวังตัวให้ดีขึ้น หากคุณต้องการป้องกันข้อผิดพลาดคำสั่ง cp สามารถดูโหนดที่เป็นปัญหาได้อย่างง่ายดายและให้พรอมต์บางสิ่งในแนวของ "นี่คือไดเรกทอรีคุณต้องการคัดลอกเนื้อหาทั้งหมดไปยังตำแหน่งที่ระบุหรือไม่ (y / n)?" ว่าจะเป็นความปลอดภัยสุทธิ การร้องขอ -r สำหรับไดเร็กทอรีจะทำให้โค้ดมีจำนวนลดลงมากกว่าสิ่งอื่นใด
JDL

12
การเปรียบเทียบไม่ดีกับท่อระบายน้ำ ในฐานะที่เป็น @JDL กล่าวว่าการตั้งค่าสถานะที่เป็นปัญหาไม่ทำอะไรเลยเพื่อป้องกันการพิมพ์ผิดในเส้นทาง ฉันยินดีที่จะยอมรับความสอดคล้องกับคำสั่งอื่น ๆ ด้วยเหตุผล แต่ฉันมีความรู้สึกเหตุผลที่แท้จริงคือ "นั่นคือวิธีที่มันถูกเขียนขึ้นครั้งแรกและตอนนี้หลายสิ่งหลายอย่างขึ้นอยู่กับพฤติกรรมนั้นเปลี่ยนเป็นไปไม่ได้"
พื้นฐาน

7
เมื่อเราย้ายไดเรกทอรีเราไม่จำเป็นต้องใช้ -r ฉันคิดว่าคำตอบที่เชื่อมโยงที่ unix.stackexchange.com เป็นสิ่งที่มากกว่านั้น เทียบเท่าของสำเนาที่ไม่เกิดซ้ำจะมีไดเรกทอรีที่สองและฮาร์ดลิงก์สำหรับไฟล์ทั้งหมดในแผนผังไดเรกทอรี
gerrit

2
ถ้าฉันไม่ผิด, -r เป็นส่วนขยายของ GNU - ฉันไม่คิดว่าประวัติศาสตร์ UNIX CP มีสำเนา recursive - และที่เป็นส่วนหนึ่งของเหตุผลสำหรับคำสั่งrsync
เหม่ย

2
@ JakeGould ฉันเข้าใจแนวคิดพื้นฐาน อย่างไรก็ตามฉันโต้เถียงกับแนวคิดที่ว่าแฟล็ก -r ในคำสั่งที่เป็นปัญหาจะช่วยเพิ่มความปลอดภัยใด ๆ จากประสบการณ์ของฉันคำสั่ง linux ของบรรทัดคำสั่งนั้นไม่ปลอดภัยโดยเนื้อแท้ ความปลอดภัยได้รับการเพิ่มการทำงานล่วงเวลาถ้าทั้งหมด ความปลอดภัยที่มีอยู่ในการออกแบบลินุกซ์มาจากระบบการอนุญาตและการดำเนินการต่อเนื่องเป็นราก การไม่ทำงานในฐานะรูทนั้นเป็นสิ่งที่มีมากกว่าแบบแผนมากกว่าการออกแบบ การประชุมได้รับการสนับสนุนในตัวติดตั้ง linux ใหม่ส่วนใหญ่ แต่ก็ไม่เสมอไป
JDL

19

เป็นความจริงที่ว่านี่เป็นพฤติกรรมที่เราต้องการเกือบตลอดเวลา นี่ไม่ได้แปลว่าการคัดลอกซ้ำ ๆ จะเป็นพฤติกรรมเริ่มต้น

ผมคิดว่าเหตุผลที่cpทำหน้าที่เป็นมันไม่มีรากในปรัชญา Unix Unix สนับสนุนโปรแกรมที่ทำสิ่งหนึ่งและทำได้ดีเช่นเดียวกับโปรแกรมที่ง่ายทั้งในส่วนต่อประสานและการนำไปใช้ (บางครั้งเรียกว่าแย่กว่านั้นดีกว่า )

ชิ้นส่วนสำคัญของปริศนาที่นี่คือการตระหนักcpว่าไม่ได้คัดลอกไดเรกทอรี - cpคัดลอกไฟล์ (และไฟล์เท่านั้น ) หากคุณต้องการคัดลอกไดเรกทอรีcp เรียกตัวเองซ้ำเพื่อคัดลอกไฟล์ในแต่ละไดเรกทอรี

แน่นอนความแตกต่างระหว่าง "การคัดลอกไดเรกทอรี" และ "คัดลอกไฟล์แบบเรียกซ้ำ" นั้นไม่ได้มีอะไรเลยจากมุมมองของผู้ใช้ แต่การมีส่วนต่อประสานนี้จะช่วยให้การติดตั้งง่ายขึ้น

หากคุณcpสามารถคัดลอกไดเรกทอรีได้ในไม่ช้าคุณจะถูกล่อลวงให้เพิ่มคุณสมบัติเพิ่มเติมที่เหมาะสมกับไดเรกทอรีเท่านั้นตัวอย่างเช่นคุณอาจต้องการคัดลอกชื่อไฟล์ที่ลงท้ายด้วย.shเท่านั้น สิ่งนี้นำไปสู่การขยายตัวและคุณสมบัติการคืบที่เราคุ้นเคยในระบบปฏิบัติการอื่น - ทำให้ซอฟต์แวร์ช้าซับซ้อนและเกิดข้อผิดพลาดได้ง่าย

ข้อดีอีกประการคือการมีส่วน-rช่วยให้ผู้ใช้เข้าใจสิ่งที่เกิดขึ้นจริงภายใต้ส่วนต่อประสาน ผลข้างเคียงที่ดีของสิ่งนี้คือการเรียนรู้แนวคิดของการทำงานแบบเรียกซ้ำจะช่วยให้คุณทำงานเมื่อคุณเรียนรู้เกี่ยวกับเครื่องมืออื่น ๆ ที่สนับสนุน (เช่นgrepเช่น)


บางคนอย่างแน่นอนจะบอกคุณว่าการเปิดเผยรายละเอียดการดำเนินงานให้กับผู้ใช้เป็นที่ไม่ดีและมีคุณสมบัติมากขึ้นเป็นสิ่งที่ดี ความตั้งใจของฉันที่นี่เป็นเพียงการอธิบายเหตุผลของพฤติกรรมนี้ดังนั้นฉันจะไม่พยายามเถียงด้วยวิธีใดวิธีหนึ่ง


2
+1 “ …ทำสิ่งหนึ่งแล้วทำมันให้ดี…”ขอบคุณที่แจ้งมา
JakeGould

5

การโต้ตอบกับไดเรกทอรีทำให้แน่ใจว่าคุณรู้ว่าคุณมีปฏิสัมพันธ์กับไดเรกทอรีและไม่ใช่แค่ไฟล์เดียว

ตัวอย่างเช่น

$ 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

ดังนั้นหากคุณต้องการประสบความสำเร็จในการคัดลอกโฟลเดอร์เพราะมันหมายถึงทั้งโฟลเดอร์และวัตถุที่เกี่ยวข้องกับการอ้างอิงโฟลเดอร์คุณจะต้องถือว่ามันเหมือนกับว่ามันเป็นชุดของไฟล์:

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

3

ผลที่ตามมาของการเปลี่ยนค่าเริ่มต้นคือเชลล์สคริปต์นับพันจะแตก สิ่งนี้นำไปสู่ข้อกำหนด POSIX และ SUS สำหรับพฤติกรรมเริ่มต้นที่รู้จักกันดี

เหตุผลก็คือการพัฒนาในอดีตของคำสั่ง cp, ln และ mv (ไบนารีเดียวกันทั้งหมดบนระบบ UNIX ที่เก่าที่สุด) ในสาขา UNIX ต่างๆ เมื่อ-rปรากฏ (ก่อนหน้าcpนี้ไม่มีตัวเลือกในการคัดลอกไดเรกทอรีนี่คือหน้า man cp ก่อนโดยไม่มี-rหรือ-R) มีความแตกต่างหลากหลายในการจัดการไฟล์พิเศษ symlink และความหลากหลายอื่น ๆ ของระบบไฟล์

จากข้อมูลจำเพาะฐานของ Open Group ฉบับที่ 7 :

เวอร์ชันก่อนหน้าของมาตรฐานนี้รวมการสนับสนุนสำหรับตัวเลือก -r เพื่อคัดลอกลำดับชั้นของไฟล์ ตัวเลือก -r คือการฝึกประวัติในระบบที่ได้จาก BSD และ BSD ตัวเลือกนี้ไม่ได้ระบุไว้โดย POSIX.1-2008 อีกต่อไป แต่อาจมีอยู่ในการใช้งานบางอย่าง ตัวเลือก -R ถูกเพิ่มเป็นคำพ้องความหมายใกล้กับตัวเลือก -r เลือกเพื่อความสอดคล้องกับตัวเลือกอื่น ๆ ทั้งหมดในเล่มนี้ของ POSIX.1-2008 ที่ทำซ้ำไดเรกทอรีสืบซ้ำ

ความแตกต่างระหว่าง -R และตัวเลือก -r ที่ถูกลบอยู่ในการจัดการโดย cp ของไฟล์ประเภทอื่นนอกเหนือจากปกติและไดเรกทอรี เป็นตัวกำหนดการนำไปใช้งาน - ตัวเลือก - จัดการไฟล์พิเศษเพื่อให้ทั้งการใช้งานในอดีตและไฟล์ที่เลือกที่จะสนับสนุน -r ด้วยความสามารถเช่นเดียวกับ -R ที่กำหนดโดยปริมาตร POSIX.1-2008 นี้ แฟล็ก -r ดั้งเดิมด้วยเหตุผลเชิงประวัติไม่ได้จัดการไฟล์พิเศษใด ๆ ที่แตกต่างจากไฟล์ปกติ แต่อ่านไฟล์และคัดลอกเนื้อหาเสมอ เรื่องนี้มีปัญหาชัดเจนในการปรากฏตัวของไฟล์ประเภทพิเศษ; ตัวอย่างเช่นอุปกรณ์ตัวอักษร FIFOs และซ็อกเก็ต

ในความเป็นจริงคุณจะยังเห็นบางคนใช้เป็นประจำ:

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

เพราะพวกเขาไม่เชื่อว่าcp -rการนำไปปฏิบัติจะเป็นสิ่งที่พวกเขาคุ้นเคยกับเครื่องคอมพิวเตอร์โดยพลการ หรือเพราะพวกเขาต้องการtarพฤติกรรม


3

มันอาจจะเป็น UI ที่ไม่ดีพอในวันนี้ แต่มันเป็นการตัดสินใจที่ใช้เวลาประมาณปี 1970 ในระหว่างการออกแบบ UNIX เมื่อดิสก์มีราคาแพงกว่ามาก เชลล์สคริปต์หลายล้านรายการอาศัยการทำงานในลักษณะนั้นและมันก็สายเกินไปที่จะเปลี่ยนแปลง

ดูบทความนี้สำหรับข้อมูลการออกแบบดั้งเดิม


3

ข้อได้เปรียบที่ชัดเจนของการ-rตั้งค่าสถานะคือคุณสามารถcp * /target/dirคัดลอกเฉพาะไฟล์ทั้งหมดในไดเรกทอรีต้นทางไปยังไดเรกทอรีเป้าหมายโดยไม่ต้องเว้นวรรค (แม้ว่าจะมีคำเตือน) ไดเรกทอรีทั้งหมดที่อยู่ในนั้น cp -r * /target/dirจะคัดลอกทุกอย่างแทนรวมถึงไดเรกทอรีย่อย


2

คุณต้องการแฟล็กนี้ต่อเมื่อcpคำสั่ง is สำหรับการคัดลอกไฟล์และไดเร็กทอรีและไม่ใช่เฉพาะไดเร็กทอรี

หากมีคำสั่งพิเศษสำหรับการคัดลอกไดเรกทอรีพฤติกรรม "เริ่มต้น" จะเป็นสำเนาที่เรียกซ้ำ


1
มีเหตุผล. แต่ทำไมบางคนถึงคัดลอกไดเรกทอรีที่ไม่มีไฟล์อย่างน้อยหนึ่งไฟล์ ทำไมไม่ลองใช้ดูmkdirล่ะ?
JakeGould

1
@ Jake อาจจะเป็นเพราะพวกเขาอาจจำเป็นต้องรักษาความเป็นเจ้าของและสิทธิ์?
Ruslan

1

ดังที่คนอื่น ๆ พูดถึงกันทั่วไปแล้วไดเรกทอรีนั้นเป็นเพียงไฟล์ประเภทอื่น (เมื่อเทียบกับไฟล์ปกติ) ซึ่งโดยปกติแล้วจะมีไฟล์ "ประกอบด้วย" (ชี้ไปที่) มันอาจมีไดเรกทอรีย่อยซึ่งเหมือนกันกับ ...

ดังนั้นหากคุณกำลังคัดลอกไดเรกทอรี (มุมมองของผู้ใช้) คุณกำลังคัดลอกไฟล์จำนวนมาก (มุมมองของระบบแฟ้ม) (ไฟล์ปกติ, ไฟล์ไดเรกทอรี, ลิงก์สัญลักษณ์, ... ) และสำหรับไฟล์ไดเรกทอรีทุกครั้งคุณจะทำซ้ำซ้ำ ๆ กระบวนการ. ตั้งแต่การคัดลอกไดเรกทอรีคือโดยความหมายกระบวนการ recursive อาร์กิวเมนต์ CP --recursiveเรียกว่า

แน่นอนว่ามันง่ายมากที่จะสร้างทางลัดคำสั่งในสภาพแวดล้อมผู้ใช้ของคุณ (ใส่ไว้ในไฟล์. profile / .bashrc ของคุณเพื่อให้สามารถใช้งานได้อย่างถาวร):

alias cpr='cp -r'

หรืออาจจะดีกว่า:

alias cpa='cp -av'

ด้วยวิธีนี้คุณสามารถคัดลอกไดเรกทอรีโดยใช้cpa dir1 copyDir1และจะไม่เพียงพิมพ์สิ่งที่คัดลอก แต่ยังใช้สิทธิ์ของไฟล์

และเนื่องจากมีคนกล่าวว่า cp สามารถตรวจพบในทางทฤษฎีว่าไฟล์ต้นฉบับนั้นเป็นไดเรกทอรีและถามว่าควรจะคัดลอกมันซ้ำ ๆ หรือไม่นี่เป็นคำแนะนำสั้น ๆ :

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

นี่เป็นเพียงกระดาษห่อ cp ราคาถูก มันจะรักษาข้อมูลเมตาทั้งหมด (เช่นคัดลอกเวลาแก้ไขไฟล์คัดลอก symlink อย่างถูกต้องและอื่น ๆ ) และถ้าคุณพยายามที่จะคัดลอกไดเรกทอรีมันจะถามว่ามันควรจะคัดลอก (เรียกซ้ำ)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.