Linux: คัดลอกและสร้างปลายทาง dir หากไม่มีอยู่


348

ฉันต้องการคำสั่ง (หรืออาจเป็นตัวเลือกให้ cp) ที่สร้างไดเรกทอรีปลายทางหากไม่มีอยู่

ตัวอย่าง:

cp -? file /path/to/copy/file/to/is/very/deep/there


5
คำตอบที่ยอมรับนั้นผิด (มีตัวเลือกสำหรับ cp) ดูคำตอบที่สองโดย Paul Whipp
Ronenz

1
@ Ronenz ฉันเห็นด้วยว่ามีตัวเลือกที่น่ากล่าวถึงใน GNU cp(ตอบโดย Paul Whipp) แต่มันไม่ได้เป็นเรื่องทั่วไปตามที่ OP ร้องขอ ก็สามารถคัดลอกa/foo/bar/fileไปb/foo/bar/fileแต่ก็ไม่สามารถคัดลอกไปfile b/foo/bar/file(เช่นมันจะทำงานเพื่อสร้างไดเรกทอรีหลักที่มีอยู่แล้วในกรณีของไฟล์แรก แต่มันไม่สามารถสร้างไดเรกทอรีโดยพลการ)
Sudo Bash

ดูเหมือนว่าคำขอคุณลักษณะที่ดีสำหรับ cp โอกาสใดที่เราจะได้สวิตช์บรรทัดคำสั่งดังกล่าวในอนาคตอันใกล้?
Arnaud

คำตอบ:


328
mkdir -p "$d" && cp file "$d"

(ไม่มีตัวเลือกดังกล่าวสำหรับcp)


62
ฉันไม่คิดว่าคุณต้องการtest -d: mkdir -pยังคงประสบความสำเร็จแม้ว่าจะมีไดเรกทอรีอยู่แล้ว
ephemient

อ๊ะขวา แต่แล้วการทดสอบอาจเป็นการทุบตีในตัว (โดยเฉพาะถ้าเขียนเป็น [[-d "$ d"]]) และ mkdir ไม่สามารถ ;-)
Michael Krelin - แฮกเกอร์

1
mkdirเป็น builtin ใน BusyBox;)
ephemient

7
@holms $dเป็นตัวแปรที่มีไดเรกทอรีน่าสงสัย ฉันหมายความว่าถ้าคุณต้องทำซ้ำสามครั้งคุณมีโอกาสที่จะกำหนดให้ตัวแปรแรก
Michael Krelin - แฮ็กเกอร์

237

หากทั้งสองอย่างต่อไปนี้เป็นจริง:

  1. คุณกำลังใช้รุ่น GNU ของcp(และไม่ใช่รุ่น Mac) และ
  2. คุณกำลังคัดลอกจากโครงสร้างไดเรกทอรีที่มีอยู่และคุณเพียงต้องการสร้างใหม่

จากนั้นคุณสามารถทำเช่นนี้กับธงชาติ--parents cpจากหน้าข้อมูล (ดูได้ที่http://www.gnu.org/software/coreutils/manual/html_node/cp-invocation.html#cp-invocationหรือด้วยinfo cpหรือman cp):

--parents
     Form the name of each destination file by appending to the target
     directory a slash and the specified name of the source file.  The
     last argument given to `cp' must be the name of an existing
     directory.  For example, the command:

          cp --parents a/b/c existing_dir

     copies the file `a/b/c' to `existing_dir/a/b/c', creating any
     missing intermediate directories.

ตัวอย่าง:

/tmp $ mkdir foo
/tmp $ mkdir foo/foo
/tmp $ touch foo/foo/foo.txt
/tmp $ mkdir bar
/tmp $ cp --parents foo/foo/foo.txt bar
/tmp $ ls bar/foo/foo
foo.txt

4
สิ่งนี้ใช้ไม่ได้กับ Mac OS X ดังนั้นฉันคิดว่าเฉพาะ Linux เท่านั้น
olt

7
ฉันจะพูดเฉพาะ gnu ถ้าคุณมีคุณสามารถติดตั้งและmacports coreutils gcp
Michael Krelin - แฮ็กเกอร์

16
ผู้ชาย, linux มีทุกสิ่ง
Ziggy

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

7
สิ่งนี้จะถือว่าไดเรกทอรี 'bar' มีอยู่แล้ว แต่คำถามเดิมแสดงถึงวิธีการสร้าง 'bar' โดยอัตโนมัติเมื่อมีการระบุไว้เป็นปลายทางและไม่มีอยู่จริง คำตอบสำหรับเรื่องนี้ให้บริการโดย @ MichaelKrelin-hacker
thdoan

69

คำตอบสั้น ๆ

ในการคัดลอกmyfile.txtไป/foo/bar/myfile.txtใช้:

mkdir -p /foo/bar && cp myfile.txt $_

มันทำงานอย่างไร

มีองค์ประกอบอยู่สองสามอย่างดังนั้นฉันจะอธิบายขั้นตอนทั้งหมดของไวยากรณ์

mkdirยูทิลิตี้ที่ระบุไว้ในมาตรฐาน POSIXทำให้ไดเรกทอรี -pโต้แย้งต่อเอกสารที่จะทำให้เกิดmkdirไป

สร้างส่วนประกอบชื่อพา ธ กลางที่ขาดหายไป

หมายความว่าเมื่อโทรmkdir -p /foo/bar, mkdirจะสร้าง/foo และ /foo/barถ้า/fooไม่ได้อยู่แล้ว (หากไม่มี-pจะเป็นการโยนข้อผิดพลาดแทน

ตัว&&ดำเนินการรายการตามที่ระบุไว้ในมาตรฐาน POSIX (หรือคู่มือ Bashหากคุณต้องการ) จะมีเอฟเฟกต์ที่cp myfile.txt $_จะเรียกใช้งานได้ก็ต่อเมื่อmkdir -p /foo/barดำเนินการสำเร็จ ซึ่งหมายความว่าcpคำสั่งจะไม่พยายามที่จะดำเนินการถ้าmkdirล้มเหลวสำหรับหนึ่งในหลายเหตุผลที่อาจล้มเหลว

ในที่สุด$_เราก็ผ่านเป็นอาร์กิวเมนต์ที่สองไปcpเป็น "พารามิเตอร์พิเศษ" ซึ่งจะมีประโยชน์สำหรับการหลีกเลี่ยงการทำซ้ำอาร์กิวเมนต์ยาว (เช่นเส้นทางไฟล์) โดยไม่ต้องเก็บไว้ในตัวแปร ตามคู่มือ Bashมัน:

ขยายไปยังอาร์กิวเมนต์สุดท้ายของคำสั่งก่อนหน้า

ในกรณีนี้ที่ว่าเราส่งผ่านไปยัง/foo/bar mkdirดังนั้นcpคำสั่งจะขยายเป็นcp myfile.txt /foo/barซึ่งคัดลอกmyfile.txtไปยัง/foo/barไดเรกทอรีที่สร้างขึ้นใหม่

ทราบว่า$_เป็นไม่ได้เป็นส่วนหนึ่งของ POSIX มาตรฐานดังนั้นในทางทฤษฎีตัวแปร Unix อาจจะมีเปลือกที่ไม่สนับสนุนการสร้างนี้ แต่ผมไม่ทราบว่าของเปลือกหอยที่ทันสมัยใด ๆ ที่ไม่สนับสนุน$_; แน่นอน Bash, Dash และ zsh ทั้งหมดทำ


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

mkdir -p "/my directory/name with/spaces" && cp "my filename with spaces.txt" "$_"

16
นอกเหนือจาก: ฉันมักจะผิดหวังในการขาดรายละเอียดของคำถามเกี่ยวกับคำสั่ง Bash หรือ Unix shell ใน Stack Overflow ซึ่งมักจะใช้การซับหนึ่งครั้งที่ซับซ้อนและมีความหนาแน่นสูงซึ่งยากที่จะคลี่คลายถ้าคุณไม่ใช่วิซาร์ด Unix ฉันได้ลองทำสิ่งที่ตรงกันข้ามกันมากที่นี่และทั้งคู่อธิบายรายละเอียดทั้งหมดของไวยากรณ์และลิงก์ไปยังสถานที่ที่เหมาะสมเพื่อค้นหาเอกสารเกี่ยวกับการทำงานของสิ่งนี้ ฉันหวังว่านี่จะช่วยคนอย่างน้อย เครดิตที่ครบกำหนด: ฉันให้วิธีนี้จากคำตอบนี้สำหรับคำถามที่ซ้ำกัน: stackoverflow.com/a/14085147/1709587
Mark Amery

ฉันไม่เคยเห็น $ _ มาก่อน มันหมายถึงอะไร โฟลเดอร์ที่ใช้ในคำสั่งสุดท้ายหรือไม่
thebunnyrules

9
@thebunnyrules แต่ ... แต่ ... มี"สิ่งนี้ทำงานอย่างไร" ในคำตอบของฉันที่มีหลายย่อหน้าโดยเฉพาะสำหรับคำถามที่คุณเพิ่งถาม มันรู้สึกเพิกเฉยและไม่มีใครรัก :(
ทำเครื่องหมาย Amery

ขอโทษด้วยกับเรื่องนั้น. คุณพูดถูก ฉันรู้แล้วทุกอย่างในคำตอบของคุณดังนั้นฉันเพิ่งสแกนมันอย่างรวดเร็ว ฉันควรอ่านมันให้ละเอียดมากขึ้น
thebunnyrules

ว้าวคุณใส่งานลงไปในคำตอบนั้นจริงๆ ... ขอบคุณสำหรับสิ่งนั้น มันเป็นเรื่องดีที่ได้เรียนรู้เกี่ยวกับ: $ _ ฉันสามารถเห็นตัวเองใช้มันได้ไม่นาน ฉันเขียนสคริปต์เพื่อทำให้กระบวนการทั้งหมดเป็นไปโดยอัตโนมัติและวางในหัวข้อนี้ ไปกันเถอะบางทีคุณอาจจะชอบ: stackoverflow.com/questions/1529946/…
thebunnyrules

39

เป็นคำถามเก่า แต่บางทีฉันสามารถเสนอทางเลือกอื่นได้

คุณสามารถใช้installโปรแกรมเพื่อคัดลอกไฟล์ของคุณและสร้างเส้นทางปลายทาง "ได้ทันที"

install -D file /path/to/copy/file/to/is/very/deep/there/file

มีบางด้านที่ต้องพิจารณาแม้ว่า:

  1. คุณต้องระบุชื่อไฟล์ปลายทางด้วยไม่ใช่แค่เส้นทางปลายทาง
  2. ไฟล์ปลายทางจะสามารถใช้งานได้ (อย่างน้อยเท่าที่ฉันเห็นจากการทดสอบของฉัน)

คุณสามารถแก้ไข # 2 ได้อย่างง่ายดายโดยการเพิ่ม-mตัวเลือกเพื่อตั้งค่าการอนุญาตในไฟล์ปลายทาง (ตัวอย่าง: -m 664จะสร้างไฟล์ปลายทางที่มีสิทธิ์rw-rw-r--เช่นเดียวกับการสร้างไฟล์ใหม่ด้วยtouch)


และนี่คือลิงค์ไร้ยางอายสู่คำตอบที่ฉันได้รับแรงบันดาลใจจาก =)


ใช้งานไม่ได้กับการใช้งานของฉัน แต่ใช้งานได้ดี! ฉันจะจำไว้
thebunnyrules

โปรดทราบว่าคำสั่งนี้สร้างไฟล์ปลายทางที่มีสิทธิ์755( rwxr-xr-x) ซึ่งอาจไม่ต้องการ คุณสามารถระบุอย่างอื่นด้วย-mสวิตช์ แต่ฉันไม่สามารถหาวิธีที่จะให้สิทธิ์การใช้งานไฟล์ :(
törzsmókus

19

ฟังก์ชั่นเชลล์ที่ทำในสิ่งที่คุณต้องการเรียกมันว่า "bury" copy เพราะมันจะขุดรูเพื่อให้ไฟล์อยู่ใน:

bury_copy() { mkdir -p `dirname $2` && cp "$1" "$2"; }

1
คุณควรจะมีเครื่องหมายคำพูดอยู่รอบ ๆdirname $2ด้วย
Tarnay Kálmán

4
@Kalmi, ใบเสนอราคาที่เหมาะสมนอกจากนี้คุณยังต้องการที่จะพูดเป็นอาร์กิวเมนต์ไปยัง$2 dirnameกดmkdir -p "$(dirname "$2")"ไลค์ โดยไม่ต้องนี้ quoting $2ในcpไร้ประโยชน์;)
ไมเคิล Krelin - แฮ็กเกอร์

3
โปรดทราบว่าคำตอบนี้ถือว่า $ 2 ยังไม่ได้เป็นไดเรกทอรีเป้าหมายมิฉะนั้นคุณเพียงต้องการ "mkdir -p" $ 2 "&& cp" $ 1 "" $ 2 "เป็นเนื้อหาของฟังก์ชั่น
467257

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

@ ที่อุดมไปด้วยฉันไม่เห็นด้วยที่ไม่สามารถแก้ไขได้ การทำงานต่อไปนี้ใช้ได้ทั้งไฟล์และไดเรกทอรี: mkdir -p dirname "$2"&& cp -r "$ 1" "$ 2"; โปรดทราบว่า backticks ที่อยู่รอบ ๆdirname $2จะไม่แสดงขึ้นเนื่องจาก SO จะแยกพวกเขาเป็นตัวทำเครื่องหมายรหัส
Yury

11

นี่คือวิธีหนึ่งในการทำ:

mkdir -p `dirname /path/to/copy/file/to/is/very/deep/there` \
   && cp -r file /path/to/copy/file/to/is/very/deep/there

dirnameจะให้แม่ของไดเรกทอรีปลายทางหรือไฟล์ mkdir -p `dirname ... 'จากนั้นจะสร้างไดเรกทอรีนั้นเพื่อให้แน่ใจว่าเมื่อคุณเรียก cp -r ไดเรกทอรีฐานที่ถูกต้องจะถูกแทนที่

ข้อได้เปรียบของส่วนที่เกิน - นี้คือมันใช้ได้กับกรณีที่องค์ประกอบสุดท้ายในเส้นทางปลายทางเป็นชื่อไฟล์

และมันจะทำงานบน OS X


6

install -D file -m 644 -t /path/to/copy/file/to/is/very/deep/there


1
จริงๆแล้วมันไม่เหมือนกันเลย เขาต้องการให้คุณส่งชื่อไฟล์สองครั้ง (ด้วยเหตุนี้แฟล็ก '-t') และจะตั้งค่าบิตเรียกใช้งานบนไฟล์ (ตัวอย่างเช่น '-m') เขาไม่ควรเป็นคำตอบที่ดีที่สุดเพราะจริงๆแล้วมันไม่ได้ตอบคำถาม - ในขณะที่ฉันทำ
Spongman

1
ใช้งานได้กับไฟล์เดียว เพียงแค่พิจารณาว่าthereเป็นไฟล์สุดท้ายไม่ใช่ไฟล์ย่อยสุดท้าย
kvantour

6

ด้วยความเคารพทั้งหมดของฉันสำหรับคำตอบข้างต้นฉันชอบที่จะใช้ rsync ดังต่อไปนี้:

$  rsync -a directory_name /path_where_to_inject_your_directory/

ตัวอย่าง:

$ rsync -a test /usr/local/lib/

ฉันลองและไปผลลัพธ์นี้: rsync -a bull / some / hoop / ti / doop / ploop ผลการสืบค้น rsync: mkdir "/ some / hoop / ti / doop / ploop" ล้มเหลว: ไม่มีไฟล์หรือไดเรกทอรี (2) rsync error : ข้อผิดพลาดในไฟล์ IO (รหัส 11) ที่ main.c (675) [ผู้รับ = 3.1.2]
thebunnyrules

@thebunny กำหนดว่าคุณต้องตรวจสอบเส้นทางด้วย คำสั่งpwd (ดูที่: [ลิงค์] ( access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/… ) และใส่คำสั่งนั้นอย่างถูกต้อง
marcdahan

หากฉันเข้าใจคำแนะนำของคุณอย่างถูกต้องคุณกำลังเสนอให้ฉันระบุเส้นทางที่สมบูรณ์ไปยังแหล่งที่มาหรือที่รู้จักในการใช้: "$ (pwd) / bull" ในคำสั่งของฉันแทนที่จะเป็นชื่อไฟล์: bull? สิ่งคือข้อผิดพลาดอยู่กับ rsync สร้างปลายทาง dir ไม่ใช่แหล่งที่มา (วัว) ดูเหมือนว่าฉัน rsync ใช้ง่าย mkdir และไม่ใช่ mkdir -p
thebunnyrules

1
ยูทิลิตี้ที่ยอดเยี่ยมโดยทางฉันอาจเริ่มใช้ในสคริปต์ของฉัน ขอบคุณสำหรับการแนะนำ
thebunnyrules

1
rsynccpเป็นที่คาดหมายมากกว่า ตัวอย่างเช่นถ้าฉันต้องการที่จะcp /from/somedir /to-dirแล้วพฤติกรรม CP แตกต่างกันถ้า/to-dirมีอยู่แล้ว: ถ้า/to-dirอยู่แล้วcpจะสร้าง/to-dir/some-dirมิฉะนั้นเพียงแค่เนื้อหาของถูกวางลง/from/somedir /to-dirอย่างไรก็ตาม rsyncพฤติกรรมเดียวกันในกรณีเช่น /to-dir/some-dirนี้จะมักจะถูกสร้างขึ้น
JoeAC

5

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

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p $(dirname ${fdst}) && cp -p ${fsrc} ${fdst}

หรือทุบตีเฉพาะ

fsrc=/tmp/myfile.unk
fdst=/tmp/dir1/dir2/dir3/myfile.txt
mkdir -p ${fdst%/*} && cp -p ${fsrc} ${fdst}

ขอบคุณ! นี่คือสิ่งที่ฉันต้องการ ในกรณีของฉันฉันไม่รู้ว่าปลายทางมีไดเรกทอรีหรือไม่และมีไดเรกทอรีอยู่หรือไม่ดังนั้นรหัสนี้จะให้ไดเรกทอรีหลักซึ่งฉันสามารถสร้างได้อย่างปลอดภัยหากไม่มีอยู่
xbmono

4

เพียงเพิ่มสิ่งต่อไปนี้ใน. bashrc ของคุณปรับแต่งหากคุณต้องการ ทำงานได้ใน Ubuntu

mkcp() {
    test -d "$2" || mkdir -p "$2"
    cp -r "$1" "$2"
}

เช่นหากคุณต้องการคัดลอกไฟล์ 'ทดสอบ' ไปยังการใช้ไดเรกทอรีปลายทาง 'd'

mkcp test a/b/c/d

mkcpจะตรวจสอบก่อนว่ามีไดเรกทอรีปลายทางอยู่หรือไม่ถ้าไม่สร้างและคัดลอกไฟล์ / ไดเรกทอรี


ตามที่คนอื่นแสดงความคิดเห็นในคำตอบอื่นคุณไม่จำเป็นต้องทดสอบว่ามีไดเรกทอรีอยู่ก่อนโทรmkdir -pหรือไม่ มันจะประสบความสำเร็จแม้ว่าจะมีอยู่แล้ว
bfontaine


3

ฉันเขียนสคริปต์สนับสนุนสำหรับ cp เรียกว่า CP (ตัวอักษรตัวใหญ่) ที่ตั้งใจจะทำสิ่งนี้ สคริปต์จะตรวจสอบข้อผิดพลาดในเส้นทางที่คุณใส่ไว้ (ยกเว้นอันสุดท้ายซึ่งเป็นปลายทาง) และหากทุกอย่างเรียบร้อยจะทำขั้นตอน mkdir -p เพื่อสร้างเส้นทางปลายทางก่อนเริ่มการคัดลอก ณ จุดนี้ยูทิลิตี้ cp ปกติจะเข้าแทนที่และสวิตช์ใด ๆ ที่คุณใช้กับ CP (เช่น -r, -p, -rpL จะถูกไพพ์ไปยัง cp โดยตรง) ก่อนที่คุณจะใช้สคริปต์ของฉันมีบางสิ่งที่คุณต้องเข้าใจ

  • ข้อมูลทั้งหมดที่นี่สามารถเข้าถึงได้โดยการทำ CP - ช่วย CP - ช่วยสวิตช์ของซีพีทั้งหมดรวมอยู่ด้วย
  • cp ปกติจะไม่ทำสำเนาหากไม่พบเส้นทางปลายทาง คุณไม่มีเครือข่ายความปลอดภัยสำหรับการพิมพ์ด้วย CP ปลายทางของคุณจะถูกสร้างขึ้นดังนั้นหากคุณสะกดผิดปลายทางเป็น / usrr / share / ไอคอนหรือ / usr / share / ไอคอนเป็นอย่างดีนั่นคือสิ่งที่จะถูกสร้างขึ้น
  • cp ปกติมีแนวโน้มที่จะสร้างแบบจำลองพฤติกรรมของมันบนเส้นทางที่มีอยู่: cp / a / b / c / d จะแตกต่างกันไปว่า d มีอยู่หรือไม่ ถ้า d เป็นโฟลเดอร์ที่มีอยู่ cp จะคัดลอก b ลงในนั้นทำ / c / d / b หากไม่มี d จะถูกคัดลอก b ไปยัง c และเปลี่ยนชื่อเป็น d หาก d มีอยู่จริง แต่เป็นไฟล์และ b เป็นไฟล์มันจะถูกเขียนทับโดยสำเนาของ b หากไม่มี c อยู่ cp จะไม่คัดลอกและออก

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

  • "CP / a / b / c / d" จะส่งผลให้ / c / d / b ถ้า d เป็นโฟลเดอร์
  • "CP / a / b / c / b" จะส่งผลให้ / c / b / b หาก b ใน / c / b เป็นโฟลเดอร์
  • หากทั้ง b และ d เป็นไฟล์: CP / a / b / c / d จะส่งผลให้ / c / d (โดยที่ d เป็นสำเนาของ b) เหมือนกันสำหรับ CP / a / b / c / b ในสถานการณ์เดียวกัน

พฤติกรรม CP เริ่มต้นนี้สามารถเปลี่ยนแปลงได้ด้วยสวิตช์ "--rename" ในกรณีนี้ก็สันนิษฐานว่า

  • "CP - ชื่อ / a / b / c / d" กำลังคัดลอก b ลงใน / c และเปลี่ยนชื่อสำเนาเป็น d

หมายเหตุการปิดบางประการ: เช่นเดียวกับ cp, CP สามารถคัดลอกหลายรายการพร้อมกันโดยมีพา ธ สุดท้ายที่ระบุว่าเป็นปลายทาง นอกจากนี้ยังสามารถจัดการเส้นทางที่มีช่องว่างตราบใดที่คุณใช้เครื่องหมายคำพูด

CP จะตรวจสอบเส้นทางที่คุณใส่และทำให้แน่ใจว่ามีอยู่ก่อนทำการคัดลอก ในโหมดเข้มงวด (มีให้ผ่าน - สวิตช์แบบเข้มงวด) ไฟล์ / โฟลเดอร์ทั้งหมดที่คัดลอกจะต้องมีอยู่หรือไม่มีการคัดลอกเกิดขึ้น ในโหมดผ่อนคลาย (- เปิดใหม่) การคัดลอกจะดำเนินการต่อหากอย่างน้อยหนึ่งรายการที่คุณระบุมีอยู่ โหมด Relaxed เป็นค่าเริ่มต้นคุณสามารถเปลี่ยนโหมดชั่วคราวผ่านสวิตช์หรือถาวรโดยการตั้งค่าตัวแปร easy_going ที่จุดเริ่มต้นของสคริปต์

นี่คือวิธีการติดตั้ง:

ในเทอร์มินัลที่ไม่ใช่รูทให้ทำ:

sudo echo > /usr/bin/CP; sudo chmod +x /usr/bin/CP; sudo touch /usr/bin/CP
gedit admin:///usr/bin/CP 

ใน gedit วางโปรแกรมอรรถประโยชน์ CP และบันทึก:

#!/bin/bash
#Regular cp works with the assumption that the destination path exists and if it doesn't, it will verify that it's parent directory does.

#eg: cp /a/b /c/d will give /c/d/b if folder path /c/d already exists but will give /c/d (where d is renamed copy of b) if /c/d doesn't exists but /c does.

#CP works differently, provided that d in /c/d isn't an existing file, it assumes that you're copying item into a folder path called /c/d and will create it if it doesn't exist. so CP /a/b /c/d will always give /c/d/b unless d is an existing file. If you put the --rename switch, it will assume that you're copying into /c and renaming the singl item you're copying from b to d at the destination. Again, if /c doesn't exist, it will be created. So CP --rename /a/b /c/d will give a /c/d and if there already a folder called /c/d, contents of b will be merged into d. 

#cp+ $source $destination
#mkdir -p /foo/bar && cp myfile "$_"

err=0 # error count
i=0 #item counter, doesn't include destination (starts at 1, ex. item1, item2 etc)
m=0 #cp switch counter (starts at 1, switch 1, switch2, etc)
n=1 # argument counter (aka the arguments inputed into script, those include both switches and items, aka: $1 $2 $3 $4 $5)
count_s=0
count_i=0
easy_going=true #determines how you deal with bad pathes in your copy, true will allow copy to continue provided one of the items being copied exists, false will exit script for one bad path. this setting can also be changed via the custom switches: --strict and --not-strict
verbal="-v"


  help="===============================================================================\
    \n         CREATIVE COPY SCRIPT (CP) -- written by thebunnyrules\
    \n===============================================================================\n
    \n This script (CP, note capital letters) is intended to supplement \
    \n your system's regular cp command (note uncapped letters). \n
    \n Script's function is to check if the destination path exists \
    \n before starting the copy. If it doesn't it will be created.\n    
    \n To make this happen, CP assumes that the item you're copying is \
    \n being dropped in the destination path and is not the destination\
    \n itself (aka, a renamed copy of the source file/folder). Meaning:\n 
    \n * \"CP /a/b /c/d\" will result in /c/d/b \
    \n * even if you write \"CP /a/b /c/b\", CP will create the path /a/b, \
    \n   resulting in /c/b/b. \n
    \n Of course, if /c/b or /c/d are existing files and /a/b is also a\
    \n file, the existing destination file will simply be overwritten. \
    \n This behavior can be changed with the \"--rename\" switch. In this\
    \n case, it's assumed that \"CP --rename /a/b /c/d\" is copying b into /c  \
    \n and renaming the copy to d.\n
    \n===============================================================================\
    \n        CP specific help: Switches and their Usages \
    \n===============================================================================\n
    \
    \n  --rename\tSee above. Ignored if copying more than one item. \n
    \n  --quiet\tCP is verbose by default. This quiets it.\n
    \n  --strict\tIf one+ of your files was not found, CP exits if\
    \n\t\tyou use --rename switch with multiple items, CP \
    \n\t\texits.\n
    \n  --relaxed\tIgnores bad paths unless they're all bad but warns\
    \n\t\tyou about them. Ignores in-appropriate rename switch\
    \n\t\twithout exiting. This is default behavior. You can \
    \n\t\tmake strict the default behavior by editing the \
    \n\t\tCP script and setting: \n
    \n\t\teasy_going=false.\n
    \n  --help-all\tShows help specific to cp (in addition to CP)."

cp_hlp="\n\nRegular cp command's switches will still work when using CP.\
    \nHere is the help out of the original cp command... \
    \n\n===============================================================================\
    \n          cp specific help: \
    \n===============================================================================\n"

outro1="\n******************************************************************************\
    \n******************************************************************************\
    \n******************************************************************************\
    \n        USE THIS SCRIPT WITH CARE, TYPOS WILL GIVE YOU PROBLEMS...\
    \n******************************************************************************\
    \n******************************* HIT q TO EXIT ********************************\
    \n******************************************************************************"


#count and classify arguments that were inputed into script, output help message if needed
while true; do
    eval input="\$$n"
    in_=${input::1}

    if [ -z "$input" -a $n = 1 ]; then input="--help"; fi 

    if [ "$input" = "-h" -o "$input" = "--help" -o "$input" = "-?" -o "$input" = "--help-all" ]; then
        if [ "$input" = "--help-all" ]; then 
            echo -e "$help"$cp_hlp > /tmp/cp.hlp 
            cp --help >> /tmp/cp.hlp
            echo -e "$outro1" >> /tmp/cp.hlp
            cat /tmp/cp.hlp|less
            cat /tmp/cp.hlp
            rm /tmp/cp.hlp
        else
            echo -e "$help" "$outro1"|less
            echo -e "$help" "$outro1"
        fi
        exit
    fi

    if [ -z "$input" ]; then
        count_i=$(expr $count_i - 1 ) # remember, last item is destination and it's not included in cound
        break 
    elif [ "$in_" = "-" ]; then
        count_s=$(expr $count_s + 1 )
    else
        count_i=$(expr $count_i + 1 )
    fi
    n=$(expr $n + 1)
done

#error condition: no items to copy or no destination
    if [ $count_i -lt 0 ]; then 
            echo "Error: You haven't listed any items for copying. Exiting." # you didn't put any items for copying
    elif [ $count_i -lt 1 ]; then
            echo "Error: Copying usually involves a destination. Exiting." # you put one item and no destination
    fi

#reset the counter and grab content of arguments, aka: switches and item paths
n=1
while true; do
        eval input="\$$n" #input=$1,$2,$3,etc...
        in_=${input::1} #first letter of $input

        if [ "$in_" = "-" ]; then
            if [ "$input" = "--rename" ]; then 
                rename=true #my custom switches
            elif [ "$input" = "--strict" ]; then 
                easy_going=false #exit script if even one of the non-destinations item is not found
            elif [ "$input" = "--relaxed" ]; then 
                easy_going=true #continue script if at least one of the non-destination items is found
            elif [ "$input" = "--quiet" ]; then 
                verbal=""
            else
                #m=$(expr $m + 1);eval switch$m="$input" #input is a switch, if it's not one of the above, assume it belongs to cp.
                switch_list="$switch_list \"$input\""
            fi                                  
        elif ! [ -z "$input" ]; then #if it's not a switch and input is not empty, it's a path
                i=$(expr $i + 1)
                if [ ! -f "$input" -a ! -d "$input" -a "$i" -le "$count_i" ]; then 
                    err=$(expr $err + 1 ); error_list="$error_list\npath does not exit: \"b\""
                else
                    if [ "$i" -le "$count_i" ]; then 
                        eval item$i="$input" 
                        item_list="$item_list \"$input\""
                    else
                        destination="$input" #destination is last items entered
                    fi
                fi
        else
            i=0
            m=0
            n=1                     
            break
        fi      
        n=$(expr $n + 1)
done

#error condition: some or all item(s) being copied don't exist. easy_going: continue if at least one item exists, warn about rest, not easy_going: exit.
#echo "err=$err count_i=$count_i"
if [ "$easy_going" != true -a $err -gt 0 -a $err != $count_i ]; then 
    echo "Some of the paths you entered are incorrect. Script is running in strict mode and will therefore exit."
    echo -e "Bad Paths: $err $error_list"
    exit
fi

if [ $err = $count_i ]; then
    echo "ALL THE PATHS you have entered are incorrect! Exiting."
    echo -e "Bad Paths: $err $error_list"
fi

#one item to one destination:
#------------------------------
#assumes that destination is folder, it does't exist, it will create it. (so copying /a/b/c/d/firefox to /e/f/firefox will result in /e/f/firefox/firefox
#if -rename switch is given, will assume that the top element of destination path is the new name for the the item being given.

#multi-item to single destination:
#------------------------------
#assumes destination is a folder, gives error if it exists and it's a file. -rename switch will be ignored.

#ERROR CONDITIONS: 
# - multiple items being sent to a destination and it's a file.
# - if -rename switch was given and multiple items are being copied, rename switch will be ignored (easy_going). if not easy_going, exit.
# - rename option but source is folder, destination is file, exit.
# - rename option but source is file and destination is folder. easy_going: option ignored.

if [ -f "$destination" ]; then
    if [ $count_i -gt 1 ]; then 
        echo "Error: You've selected a single file as a destination and are copying multiple items to it. Exiting."; exit
    elif [ -d "$item1" ]; then
        echo "Error: Your destination is a file but your source is a folder. Exiting."; exit
    fi
fi
if [ "$rename" = true ]; then
    if [ $count_i -gt 1 ]; then
        if [ $easy_going = true ]; then
            echo "Warning: you choose the rename option but are copying multiple items. Ignoring Rename option. Continuing."
        else
            echo "Error: you choose the rename option but are copying multiple items. Script running in strict mode. Exiting."; exit
        fi
    elif [ -d "$destination" -a -f "$item1" ]; then
        echo -n "Warning: you choose the rename option but source is a file and destination is a folder with the same name. "
        if [ $easy_going = true ]; then
            echo "Ignoring Rename option. Continuing."
        else
            echo "Script running in strict mode. Exiting."; exit
        fi
    else
        dest_jr=$(dirname "$destination")
        if [ -d "$destination" ]; then item_list="$item1/*";fi
        mkdir -p "$dest_jr"
    fi
else
    mkdir -p "$destination"
fi

eval cp $switch_list $verbal $item_list "$destination"

cp_err="$?"
if [ "$cp_err" != 0 ]; then 
    echo -e "Something went wrong with the copy operation. \nExit Status: $cp_err"
else 
    echo "Copy operation exited with no errors."
fi

exit

นี่อาจจะเป็น overkill แต่ก็ค่อนข้างมีประสิทธิภาพใช้งานได้ตามที่คาดหวัง
Xedret

2

cp มีหลายประเพณี:

$ cp --help
Usage: cp [OPTION]... [-T] SOURCE DEST
  or:  cp [OPTION]... SOURCE... DIRECTORY
  or:  cp [OPTION]... -t DIRECTORY SOURCE...
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

@ AndyRoss คำตอบสำหรับการทำงาน

cp SOURCE DEST

สไตล์ของcpแต่ทำสิ่งผิดถ้าคุณใช้

cp SOURCE... DIRECTORY/

cpรูปแบบของ

ฉันคิดว่า "DEST" คลุมเครือโดยไม่มีเครื่องหมายทับท้ายในการใช้งานนี้ (เช่นที่ไม่มีไดเรกทอรีเป้าหมายอยู่) ซึ่งอาจเป็นสาเหตุที่cpไม่เคยเพิ่มตัวเลือกสำหรับสิ่งนี้

ดังนั้นนี่คือเวอร์ชั่นของฟังก์ชั่นนี้ซึ่งบังคับให้ใช้เครื่องหมายทับต่อท้ายบน dest dir:

cp-p() {
  last=${@: -1}

  if [[ $# -ge 2 && "$last" == */ ]] ; then
    # cp SOURCE... DEST/
    mkdir -p "$last" && cp "$@"
  else
    echo "cp-p: (copy, creating parent dirs)"
    echo "cp-p: Usage: cp-p SOURCE... DEST/"
  fi
}

2

เพิ่งมีปัญหาเดียวกัน แนวทางของฉันคือเพียงแค่ tar ไฟล์ลงในไฟล์เก็บถาวรเช่น:

tar cf your_archive.tar file1 /path/to/file2 path/to/even/deeper/file3

tar จัดเก็บไฟล์โดยอัตโนมัติในโครงสร้างที่เหมาะสมภายในไฟล์เก็บถาวร ถ้าคุณวิ่ง

tar xf your_archive.tar

ไฟล์จะถูกแตกลงในโครงสร้างไดเรกทอรีที่ต้องการ


1

ตามที่แนะนำข้างต้นโดย help_asap และ spongeman คุณสามารถใช้คำสั่ง 'ติดตั้ง' เพื่อคัดลอกไฟล์ไปยังไดเรกทอรีที่มีอยู่หรือสร้างสร้างไดเรกทอรีปลายทางใหม่หากยังไม่มีอยู่

ตัวเลือกที่ 1 install -D filename some/deep/directory/filename
คัดลอกไฟล์ไปยังไดเรกทอรีใหม่หรือที่มีอยู่และให้สิทธิ์การใช้งานชื่อไฟล์เริ่มต้น 755

ตัวเลือก 2 install -D filename -m640 some/deep/directory/filename
ตามตัวเลือก 1 แต่ให้สิทธิ์ชื่อไฟล์ 640

ตัวเลือกที่ 3 install -D filename -m640 -t some/deep/directory/
ตามตัวเลือกที่ 2 แต่ชื่อไฟล์เป้าหมายในไดเรกทอรีเป้าหมายดังนั้นชื่อไฟล์ไม่จำเป็นต้องเขียนทั้งในแหล่งที่มาและเป้าหมาย

ตัวเลือก 4 install -D filena* -m640 -t some/deep/directory/
ตามตัวเลือก 3 แต่ใช้สัญลักษณ์แทนสำหรับหลายไฟล์

มันทำงานได้ดีใน Ubuntu และรวมสองขั้นตอน (การสร้างไดเรกทอรีแล้วคัดลอกไฟล์) เป็นขั้นตอนเดียว


1

นี่มันสายมากแล้ว แต่มันอาจช่วยมือใหม่ที่ไหนซักแห่ง หากคุณต้องการ 'auto' สร้างโฟลเดอร์ rsync ควรเป็นเพื่อนที่ดีที่สุดของคุณ rsync / path / to / sourcefile / path / to / tragetdir / thatdoestexist /


0
rsync file /path/to/copy/file/to/is/very/deep/there

rsyncงานนี้อาจถ้าคุณมีสิทธิชนิดของ


สิ่งนี้ใช้ไม่ได้สำหรับฉัน - ฉันได้รับ "ไม่มีไฟล์หรือไดเรกทอรี" บนปลายทางปลายทาง
Rich

0

คัดลอกจากต้นทางไปยังพา ธ ที่ไม่มีอยู่

mkdir p /destination && cp r /source/ $_

หมายเหตุ: คำสั่งนี้คัดลอกไฟล์ทั้งหมด

cp –r สำหรับการคัดลอกโฟลเดอร์และเนื้อหาทั้งหมด

$_ ทำงานเป็นปลายทางซึ่งสร้างขึ้นในคำสั่งสุดท้าย



-1

สมมติว่าคุณกำลังทำอะไรบางอย่างเช่น

cp file1.txt A / B / C / D / file.txt

โดยที่ A / B / C / D เป็นไดเรกทอรีที่ยังไม่มี

ทางออกที่เป็นไปได้มีดังนี้

DIR=$(dirname A/B/C/D/file.txt)
# DIR= "A/B/C/D"
mkdir -p $DIR
cp file1.txt A/B/C/D/file.txt

หวังว่าจะช่วยได้!

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