สถานการณ์:
$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat ‘foo/*’: No such file or directory)
ฉันมีไดเรกทอรีที่เต็มไปด้วยโฟลเดอร์และไฟล์ที่ซ่อนอยู่ เกิดอะไรขึ้นและทางออกคืออะไร?
สถานการณ์:
$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat ‘foo/*’: No such file or directory)
ฉันมีไดเรกทอรีที่เต็มไปด้วยโฟลเดอร์และไฟล์ที่ซ่อนอยู่ เกิดอะไรขึ้นและทางออกคืออะไร?
คำตอบ:
คำเตือน: คำตอบนี้เกี่ยวข้องกับ Bash โดยเฉพาะ แต่ส่วนใหญ่เกี่ยวข้องกับคำถามเกี่ยวกับรูปแบบ glob!
ตัวละครดาว ( *) เป็นสัญลักษณ์แทน มีชุดอักขระบางตัวที่จะแทนที่และอักขระตัวแรกที่เป็นจุด ( .) ไม่ใช่หนึ่งในนั้น นี่เป็นกรณีพิเศษเนื่องจากวิธีการทำงานของระบบไฟล์ Unix ไฟล์ที่ขึ้นต้นด้วยจุดนั้นถือว่าเป็น "ซ่อน" นั่นหมายความว่าเครื่องมือเช่นcp, lsฯลฯ จะไม่ได้ "เห็น" พวกเขาจนกว่าบอกอย่างชัดเจนที่จะทำเช่นนั้น
ก่อนอื่นเรามาสร้างข้อมูลตัวอย่าง
$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}
ดังนั้นตอนนี้เรามีดังต่อไปนี้:
$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3
ตอนนี้มาเล่นเกมกันบ้าง คุณสามารถใช้คำสั่งechoเพื่อแสดงรายการไวด์การ์ด ( *) เฉพาะสำหรับคำสั่งที่กำหนดดังนี้:
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3
$ echo reg*
regdir1 regdir2 regfile1 regfile2 regfile3
$ echo .*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2
$ echo .* *
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
$ echo .dotdir*
.dotdir1 .dotdir2
คุณสามารถใช้คำสั่งshopt -s dotglobที่จะเปลี่ยนพฤติกรรมของ*เพื่อที่นอกเหนือไปจากไฟล์เช่นมันจะตรงกับregfile1.dotfile1
ตัดตอนมาจากbashหน้าคน
dotglob If set, bash includes filenames beginning with a `.' in the results
of pathname expansion.
ตัวอย่าง:
$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
คุณสามารถย้อนกลับพฤติกรรมนี้ได้ด้วยคำสั่งนี้:
$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3
สำหรับคุณคุณกำลังบอกcpว่าคุณต้องการคัดลอกไฟล์ทั้งหมดที่ตรงกับรูปแบบ*และไม่มีไฟล์ใด ๆ
$ cp foo/.* .
หรือคุณสามารถทำได้ถ้าคุณต้องการทุกอย่างในfooโฟลเดอร์:
$ cp foo .
หรือคุณชัดเจน:
$ cp foot/.* foo/* .
รูปแบบกะทัดรัดมากขึ้นโดยใช้การขยายรั้งในbash:
$ cp foo/{.,}* .
เมื่อใดก็ตามที่คุณสามารถใช้echoเคล็ดลับเพื่อดูรูปแบบไฟล์ที่คุณนำเสนอ
$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3
อนึ่งหากคุณกำลังจะคัดลอกไดเรกทอรีของไฟล์ + ไดเรกทอรีอื่น ๆ คุณมักจะต้องการทำสิ่งนี้ซ้ำแล้วซ้ำอีกนั่นคือ-Rสวิตช์ไปที่cp:
$ cp -R foo/. .
shopt -s dotglob, echo *จากนั้นให้:.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
cp -r foo .โดยทั่วไปจะไม่สามารถทำงานได้เนื่องจากcpจะปฏิเสธที่จะคัดลอกมากกว่าตัวเองคุณอาจจะหมายถึงfoo cp -R foo/. .(โปรดทราบว่าcp -Rเป็นมาตรฐานหนึ่ง)
zshไม่รวม.และในการขยายตัวของ.. .*ในzshหากรูปแบบไม่ตรงกันคำสั่งจะถูกยกเลิก (ค่อนข้างสมเหตุสมผลอีกครั้ง) ดังนั้นในกรณีทั่วไปcp foo/{,.}* .จะล้มเหลวหากไม่มีไฟล์ที่ซ่อนอยู่หรือไม่มีไฟล์ที่ไม่ถูกซ่อน
shoptไม่มีอยู่จริง
ด้วยzshวิธีทั่วไปคือการใช้โปรแกรมขยายD รอบตัว (เพื่อรวมไฟล์ [D] ot):
cp foo/*(D) .
โปรดทราบว่ามีหรือไม่มีDzsh globs จะไม่รวม.หรือไม่..(ตามที่คุณคาดหวัง)
เชลล์ดำเนินการกับไฟล์เหล่านั้นว่าซ่อนอยู่เมื่อแก้ไข*อักขระดังนั้นจึงcpไม่ได้รับชื่อไฟล์ใด ๆ เหล่านี้เป็นอาร์กิวเมนต์
คุณสามารถคัดลอกพวกเขาโดยระบุอย่างชัดเจน cp foo/.* .
หากสิ่งที่คุณต้องการทำคือคัดลอกไฟล์ & ไดเรกทอรีทั้งหมดจากที่หนึ่งไปอีกที่หนึ่งคุณสามารถใช้rsyncคำสั่งมาตรฐาน ในตัวอย่างที่คุณให้ไว้ข้างต้น:
mkdir foo && touch foo/.test
rsync -a foo/ .
จะทำการคัดลอกเนื้อหาทั้งหมดซ้ำfooรวมถึงไฟล์ที่ซ่อนอยู่และไดเรกทอรีที่ซ่อนอยู่ลงในไดเรกทอรีปัจจุบัน เครื่องหมายทับท้ายที่foo/มีความสำคัญในการ rsync; กับมันเท่านั้นเนื้อหาของfooจะถูกคัดลอกโดยไม่ต้อง rsync จะคัดลอกfooเช่นกัน ตัวอย่างเช่น:-
mkdir src && mkdir dest && touch src/.test
rsync -a src dest // copies 'src' contents to 'dest/src'
rsync -a src/ dest // copies 'src' contents to 'dest'
มีตัวเลือกอื่น ๆ ที่ปรับแต่ง rsync รวมถึงการคัดลอกระหว่างเครื่อง
คำตอบ zsh ของStéphane Chazelas นั้นถูกต้องสำหรับนิพจน์เดียวที่มี glob ในนั้น อย่างไรก็ตามหากคุณต้องการตั้งค่าสำหรับนิพจน์ทั้งหมดตามค่าเริ่มต้นคุณสามารถใช้
setopt GLOB_DOTS
ใส่ไว้ในของคุณ~/.zshrcเพื่อให้ถาวร
dotglobจะแปลให้globdotsโดยzshซึ่งเป็นชื่อตัวเลือกที่ถูกต้องสำหรับการทุบตี