Khi nào chuỗi rỗng biểu thị thư mục hiện tại?


8

Trong một tập lệnh tôi sử dụng findđể thu thập một số tệp trong thư mục hiện tại, như trong

$ find . -name "*.h"
./foo.h

Bây giờ tôi muốn nó chỉ xuất ra foo.h, không có ./tiền tố. Tôi nghĩ rằng chuỗi rỗng ""biểu thị thư mục hiện tại trong các lệnh shell. Nhưng điều này mang lại:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

Vì vậy, tôi đã sai. Bây giờ câu hỏi của tôi là khi nào / làm thế nào / ở đâu / .. một "chuỗi rỗng (?)" Biểu thị thư mục hiện tại trong các lệnh mong đợi tên tệp hoặc tên đường dẫn? Có một lời giải thích gọn gàng và khai sáng?

Một câu hỏi bên là liệu tìm soi mói trên có thể được giải quyết đơn giản, mà không cần thao tác chuỗi a la ${parameter#word}hay cuthay sed?


1
findcó tham số -printf để thao tác cách hiển thị kết quả. find . -name '*.h' -printf '%P\n'sẽ loại bỏ ./tiền tố. Xem những gì find . -name '*.h' -printkhông.
Valentin Bajrami

Cảm ơn! Quá tệ, phiên bản tìm kiếm của tôi không hỗ trợ -printftùy chọn. FWIW, stringstrên phát hiện của tôi cho @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
phs

5
@phs đó là loại điều cần thiết mà bạn luôn đề cập đến hệ điều hành của mình.
terdon

Câu trả lời:


11

Một thời gian dài trước đây (trong phiên bản thứ 7 , 32V , 4.2BSD , 4.3BSD ), ở cấp độ hệ thống gọi tên đường dẫn zero-length ký hiệu là thư mục làm việc hiện tại (khi sử dụng cho tra cứu, nó đã không được công nhận khi cố gắng tạo hoặc xóa tập tin hoặc thư mục). Trong Hệ thống III , đã xảy ra lỗi khi sử dụng tên đường dẫn có độ dài bằng không trong mọi trường hợp và tiêu chuẩn POSIX có điều này để nói về độ phân giải tên đường dẫn:

Một tên đường dẫn null sẽ không được giải quyết thành công.


3
Một chuỗi rỗng trong $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (và các chuỗi khác nhưng không nhất thiết là tất cả các chuỗi $ * PATH) biểu thị thư mục hiện tại. dir/hầu hết giống nhưdir/.
Stéphane Chazelas

4

Bạn có thể dùng:

find * -name "*.h"

Lưu ý rằng các tệp trong thư mục hiện tại có tên bắt đầu .sẽ bị bỏ qua và các tệp có tên bắt đầu -sẽ được hiểu là các tùy chọn findvà gây ra sự tàn phá, vì vậy đây không phải là tương đương chung find . ….

Sự vắng mặt của một chuỗi kết thúc bằng " /" như là một phần của tên tệp ngụ ý thư mục hiện tại, nhưng điều đó không có nghĩa là thư mục hiện tại được biểu thị bằng một chuỗi trống (được cho là không giống như sự vắng mặt của một chuỗi, mặc dù nó có thể trông giống nhau khi được in).


4

Nói chung, chuỗi rỗng không biểu thị thư mục hiện tại, không phải lệnh shell cũng như trong các cuộc gọi hệ thống. Nó đã làm trên một số hệ thống cũ, nhưng không phải trên các hệ thống tuân thủ POSIX .

Thỉnh thoảng bạn sẽ tìm thấy một chương trình sử dụng thư mục hiện tại khi bạn vượt qua một chuỗi trống và chương trình mong đợi một tên thư mục. Điều này đôi khi có chủ ý và đôi khi là tác dụng phụ của việc chuẩn bị đường dẫn tuyệt đối của thư mục hiện tại khi chuỗi đã cho không bắt đầu bằng dấu gạch chéo.

Điều tốt nhất cho bạn sẽ là rời khỏi ./. Nó không làm hại gì cả.

Nếu danh sách các tập tin là cho

find . … | sed 's!^\./!!'

Lưu ý rằng điều này mang một số tên tập tin có chứa dòng mới. Điều này thường không phải là vấn đề đối với tiêu dùng của con người và đầu ra findkhông phù hợp cho tiêu dùng chương trình vì nó không rõ ràng. Nếu bạn đang sử dụng -print0, phù hợp với mức tiêu thụ chương trình, có lẽ bạn không quan tâm đến ./tiền tố.

Bạn có thể sử dụng find * …thay vì find . …, nhưng lưu ý rằng find *có một số khiếm khuyết khiến nó không phù hợp nói chung:

  • . được bỏ qua.
  • Tất cả các tệp chấm (tệp có tên bắt đầu bằng .hoặc ..`) bị bỏ qua.
  • Nếu có một tên tệp trong thư mục hiện tại có tên bắt đầu bằng -(hoặc một tệp được gọi !hoặc (...), nó sẽ được hiểu là một tùy chọn hoặc vị ngữ theo find.

Điểm đầu tiên không quan trọng nếu bộ lọc của bạn loại trừ thư mục hiện tại. Đối với điểm thứ hai, bạn có thể sử dụng các mẫu ..?* .[!.]* *để khớp với tất cả các tệp trong thư mục hiện tại, nhưng bạn sẽ cần kiểm tra xem mỗi mẫu có khớp với ít nhất một tệp không và bỏ qua nếu không. Điều này là có thể nhưng rất cồng kềnh. Điểm cuối cùng là một nút chặn. Vì vậy, find *có thể phù hợp để sử dụng dòng lệnh quickie, nhưng không sử dụng nó trong tập lệnh.

Một cách tiếp cận khác là sử dụng cơ sở khai thác đệ quy của vỏ, ví dụ

printf '%s\n' **/*.h

Điều này cần được kích hoạt bằng shopt -s globstarbash và set -o globstartrong ksh93, và không tồn tại trong vỏ POSIX cơ bản như dấu gạch ngang. Các tệp chấm sẽ không được duyệt theo mặc định; để bao gồm chúng, trước tiên hãy tạo toàn cầu không bỏ qua các tệp chấm với shopt -s dotglobtrong bash hoặc FIGNORE='@(.|..)'trong ksh93. Ngoài ra, nếu không có kết quả khớp, thì lệnh này sẽ in mẫu; shopt -s nullglobthay vào đó hãy chạy trong bash để in một dòng trống và sử dụng mẫu ~(N)**/*.htrong ksh.

Trong zsh, mặc định đệ quy được bật theo mặc định. Sử dụng vòng loại toàn cầu Dđể bao gồm các tệp chấm và Nđể in một dòng trống nếu không có kết quả khớp (theo mặc định, zsh sẽ đưa ra lỗi nếu một mẫu không khớp với bất kỳ tệp nào). Bạn có thể sử dụng printfnhư trên hoặc

print -rl -- **/*.h(DN)

Cảm ơn tất cả những lời khuyên này. Hầu hết các khuyết điểm find *là mới đối với tôi, và một số là đáng sợ !!
phs

2

Tôi không thể nghĩ ra bất kỳ ví dụ nào trong đó chuỗi rỗng biểu thị thư mục hiện tại .. Bạn có thể nghĩ về các yêu cầu như thế ls, nhưng đó là bởi vì lsgiả sử thư mục hiện tại nếu không có tham số nào được đưa ra và trên thực tế, nó sẽ không lấy chuỗi trống:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory

3
Một ví dụ trong đó chuỗi rỗng biểu thị thư mục hiện tại là một PATHthành phần.
hvd

0

Có nhiều thủ thuật khác nhau mà bạn có thể sử dụng để có được đầu ra của find mà không dẫn đầu ./:

  1. Sử dụng -printftùy chọn find và bảo nó chỉ in %f. Xem man find:

    % f Tên tệp với bất kỳ thư mục hàng đầu nào bị xóa (chỉ phần tử cuối cùng).

    Ví dụ:

    find . -name "*.h" -printf "%f\n"
    
  2. Phân tích đầu ra:

    find . -name "*.h" | sed 's#^./##'
    
  3. @ Trò lừa của Anthon

    find * -name "*.h"
    

Đối với câu hỏi khác của bạn, chuỗi trống không bao giờ biểu thị thư mục hiện tại. Chỉ là các chương trình khác nhau lấy thư mục hiện tại làm mặc định nên khi bạn chạy chúng mà không có đối số, chúng sẽ chạy trên thư mục hiện tại.


Điều đó giả định GNU find. Hoặc sử dụng %Pđể chỉ tước./
Stéphane Chazelas

1
find * -name '*.h'sẽ tìm thấy foo/.bar.h, nhưng không có .bar.htrong thư mục hiện tại.
Stéphane Chazelas

0

Nếu các tập tin nằm trong thư mục hiện tại, bạn chỉ có thể sử dụng ls:

$ ls *.sh

Nếu bạn cũng muốn các tệp trong thư mục con và chỉ cần tên tệp, bạn có thể làm một cái gì đó như:

$ find . -name '*.h' -exec basename {} \;

Có các lệnh sẽ nhận được sự vắng mặt của một chuỗi như thư mục hiện tại, nhưng về cơ bản vì chúng sẽ lấy thư mục hiện tại làm mặc định nếu không có gì được nhập. Các .luôn biểu thị thư mục hiện hành (và ..các thư mục cha)


OP chỉ muốn ./ bị xóa, trong khi findgiải pháp của bạn xóa tất cả các thành phần thư mục
artm
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.