สถานการณ์:
$ 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) .
โปรดทราบว่ามีหรือไม่มีD
zsh 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ซึ่งเป็นชื่อตัวเลือกที่ถูกต้องสำหรับการทุบตี