cp ẩn tập tin với các mẫu toàn cầu


13

Tình hình :

$ mkdir foo && touch foo/.test
$ cp foo/* .
zsh: no matches found: foo/*
(or bash : cp: cannot stat foo/*’: No such file or directory)

Tôi có một thư mục chứa đầy các thư mục và tập tin ẩn. Điều gì đang xảy ra và giải pháp là gì?


dotglobđược phiên dịch sang globdotsbởi zsh đó là tên tùy chọn không hợp lệ cho bash .

Câu trả lời:


19

Tuyên bố miễn trừ trách nhiệm: Câu trả lời này liên quan đến Bash cụ thể nhưng phần lớn câu hỏi áp dụng cho câu hỏi liên quan đến các mẫu toàn cầu!

Nhân vật ngôi sao ( *) là một ký tự đại diện. Có một tập hợp các ký tự nhất định sẽ thay thế và ký tự đầu tiên là dấu chấm ( .) không phải là một trong số chúng. Đây là trường hợp đặc biệt chỉ vì cách các hệ thống tệp Unix hoạt động, các tệp bắt đầu bằng dấu chấm được coi là "ẩn". Điều đó có nghĩa rằng các công cụ như cp, ls, vv sẽ không "nhìn thấy" họ trừ khi nói một cách rõ ràng để làm như vậy.

Ví dụ

Trước tiên hãy tạo một số dữ liệu mẫu.

$ mkdir .dotdir{1,2} regdir{1,2}
$ touch .dotfile{1,2} regfile{1..3}

Vì vậy, bây giờ chúng ta có những điều sau đây:

$ tree -a
.
|-- .dotdir1
|-- .dotdir2
|-- .dotfile1
|-- .dotfile2
|-- regdir1
|-- regdir2
|-- regfile1
|-- regfile2
`-- regfile3

Bây giờ hãy chơi một số trò chơi. Bạn có thể sử dụng lệnh echođể liệt kê một ký tự đại diện cụ thể ( *) sẽ là gì đối với một lệnh đã cho như vậy:

$ 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

Thay đổi hành vi?

Bạn có thể sử dụng lệnh shopt -s dotglobđể thay đổi hành vi của *nó để ngoài các tệp như regfile1nó cũng sẽ khớp .dotfile1.

đoạn trích từ bashtrang người đàn ông

dotglob If set, bash includes filenames beginning with a `.' in the results 
        of pathname expansion.

Thí dụ:

$ shopt -s dotglob
$ echo *
.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3

Bạn có thể hoàn nguyên hành vi này bằng lệnh này:

$ shopt -u dotglob
$ echo *
regdir1 regdir2 regfile1 regfile2 regfile3

Hoàn cảnh của bạn?

Đối với bạn, bạn đang nói cprằng bạn muốn sao chép tất cả các tệp phù hợp với mẫu *và không có bất kỳ tệp nào.

$ cp foo/.* .

Hoặc bạn có thể làm điều này nếu bạn muốn mọi thứ trong foothư mục:

$ cp foo .

Hoặc bạn có thể rõ ràng:

$ cp foot/.* foo/* .

Một hình thức nhỏ gọn hơn bằng cách sử dụng mở rộng nẹp trong bash:

$ cp foo/{.,}* .

Bất cứ lúc nào bạn cũng có thể sử dụng echothủ thuật để xem các mẫu tệp được đề xuất của bạn (đó là thuật ngữ ưa thích cho những gì ngôi sao là một phần của).

$ echo {.,}*
. .. .dotdir1 .dotdir2 .dotfile1 .dotfile2 abc regdir1 regdir2 regfile1 regfile2 regfile3

Ngẫu nhiên nếu bạn định sao chép một thư mục tệp + thư mục khác, bạn thường muốn thực hiện việc này một cách đệ quy, đó là -Rchuyển sang cp:

$ cp -R foo/. .

1
Bạn có thể muốn đề cập đến việc sử dụng shopt -s dotglob, echo *sau đó đưa ra:.dotdir1 .dotdir2 .dotfile1 .dotfile2 regdir1 regdir2 regfile1 regfile2 regfile3
Drav Sloan

@DravSloan - cảm ơn vì lời đề nghị. Đã thêm nó.
slm

cp -r foo .thông thường sẽ không hoạt động vì cpsẽ từ chối sao chép foochính nó, có lẽ bạn có nghĩa là cp -R foo/. .. (lưu ý đó cp -Rlà tiêu chuẩn).
Stéphane Chazelas

1
Cũng lưu ý rằng (khá hợp lý), zshkhông bao gồm ...trong việc mở rộng .*. Trong zsh, nếu một mẫu không khớp, lệnh sẽ bị hủy bỏ (khá hợp lý trở lại), vì vậy trong trường hợp chung, cp foo/{,.}* .sẽ thất bại nếu không có tệp ẩn hoặc không có tệp không bị ẩn.
Stéphane Chazelas

7
Câu trả lời này chỉ áp dụng cho bash. Người hỏi đang sử dụng zsh, trong đó câu trả lời đơn giản hơn nhiềushoptkhông tồn tại.
Gilles 'SO- ngừng trở nên xấu xa'

29

Với zsh, cách điển hình là sử dụng D vòng loại toàn cầu (bao gồm các tệp ot [D]):

cp foo/*(D) .

Lưu ý rằng có hoặc không có D, các khối zsh không bao giờ bao gồm .cũng không ..(như bạn mong đợi).


4

Shell đang coi các tệp đó là ẩn khi giải quyết *ký tự, do cpđó không nhận bất kỳ tên tệp nào trong số này làm đối số.

Bạn có thể sao chép chúng bằng cách chỉ định rõ ràng cp foo/.* .


1

Nếu những gì bạn muốn làm là sao chép tất cả các tệp và thư mục từ nơi này sang nơi khác, bạn có thể sử dụng rsynclệnh tiêu chuẩn . Trong ví dụ bạn đưa ra ở trên:

mkdir foo && touch foo/.test
rsync -a foo/ .

sẽ sao chép đệ quy tất cả các nội dung của foo, bao gồm các tệp ẩn và thư mục ẩn, vào thư mục hiện tại. Dấu gạch chéo ở cuối foo/là rất quan trọng đối với rsync; với nó chỉ có nội dung foođược sao chép, mà không có nó rsync cũng sẽ sao chép foo. Ví dụ:-

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' 

Có nhiều tùy chọn khác có sẵn tinh chỉnh rsync, bao gồm sao chép giữa các máy.


1

Câu trả lời zsh của Stéphane Chazelas là chính xác cho một biểu thức duy nhất có một hình cầu trong đó. Tuy nhiên, nếu bạn muốn đặt nó cho tất cả các biểu thức theo mặc định, bạn có thể sử dụng

setopt GLOB_DOTS

Đặt nó trong của bạn ~/.zshrcđể làm cho nó vĩnh viễn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.