Làm cách nào để chỉ lấy tên tệp với Linux 'find'?


266

Tôi đang sử dụng find cho tất cả các tệp trong thư mục, vì vậy tôi nhận được một danh sách các đường dẫn. Tuy nhiên, tôi chỉ cần tên tập tin. tức là tôi nhận được ./dir1/dir2/file.txtvà tôi muốn có đượcfile.txt

Câu trả lời:


359

Trong GNU findbạn có thể sử dụng -printftham số cho điều đó, ví dụ:

find /dir1 -type f -printf "%f\n"

9
Rõ ràng câu trả lời , nhưng nó thiếu chi tiết.
Jason McCreary

25
cái này chỉ dành cho GNU
Osama F Elias

3
Điều này không hiệu quả với tôi khi tôi sử dụng nhiều loại tệp (-o switch)
Urchin

9
tìm: -printf: không rõ chính hoặc toán tử
holms

@Urchin Không có lý do gì không nên miễn là bạn có logic chính xác (nghĩa là -ocó độ ưu tiên thấp hơn hàm ý -a, vì vậy bạn sẽ thường muốn nhóm các -ođối số của mình )
Phục hồi lại vào

157

Nếu tìm thấy của bạn không có tùy chọn -printf, bạn cũng có thể sử dụng tên cơ sở:

find ./dir1 -type f -exec basename {} \;

17
Trích dẫn dấu chấm phẩy là một cách khác để định hướng:... {} ';'
davidchambers

28

Sử dụng -execdirtự động giữ tệp hiện tại {}, ví dụ:

find . -type f -execdir echo '{}' ';'

Bạn cũng có thể sử dụng $PWDthay vì .(trên một số hệ thống, nó sẽ không tạo thêm một dấu chấm ở phía trước).

Nếu bạn vẫn có thêm một dấu chấm, thay vào đó bạn có thể chạy:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

Chính -execdirlà giống hệt với -execchính với ngoại lệ rằng tiện ích sẽ được thực thi từ thư mục chứa tệp hiện tại .

Khi được sử dụng +thay thế ;, sau đó {}được thay thế bằng càng nhiều tên đường dẫn càng tốt cho mỗi lần gọi tiện ích. Nói cách khác, nó sẽ in tất cả tên tệp trong một dòng.


Tôi đang nhận được ./filenamethay vì filename. Tùy thuộc vào nhu cầu của bạn, nó có thể hoặc không thể tốt.
dùng276648

@ user276648 Hãy thử với $PWDthay vì ..
kenorb

24

Nếu bạn đang sử dụng GNU, hãy tìm

find . -type f -printf "%f\n"

Hoặc bạn có thể sử dụng ngôn ngữ lập trình như Ruby (1.9+)

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

Nếu bạn thích một giải pháp bash (ít nhất 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done

Tôi đã được truyền cảm hứng từ câu trả lời của bạn để giúp sleske: serverfault.com/a/745968/329412
Nolwennig

11

Nếu bạn chỉ muốn chạy một số hành động đối với tên tệp, việc sử dụng basenamecó thể khó khăn.

Ví dụ này:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

sẽ chỉ echo tên cơ sở /my/found/path. Không phải những gì chúng ta muốn nếu chúng ta muốn thực hiện trên tên tệp.

Nhưng bạn có thể sau đó xargsđầu ra. ví dụ để giết các tệp trong một thư mục dựa trên tên trong một thư mục khác:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm

không lặp lại -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
thường

7

Trên mac (BSD find) sử dụng:

find /dir1 -type f -exec basename {} \;

1

-exec-execdirlà chậm, xargslà vua.

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargssong song cũng giúp.

Đủ vui tôi không thể giải thích trường hợp cuối cùng xargsmà không có -n1. Nó cho kết quả chính xác và nhanh nhất¯\_(ツ)_/¯

( basenamechỉ mất 1 đối số đường dẫn nhưng xargssẽ gửi tất cả (thực tế là 5000) mà không có -n1. không hoạt động trên linux và openbsd, chỉ macOS ...)

Một số số lớn hơn từ hệ thống linux để xem cách -execdirgiúp, nhưng vẫn chậm hơn nhiều so với song song xargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system

1
chỉ cần thêm một điểm dữ liệu: trên openbsd trong một thời gian dài, find-execdirtrở nên nhanh nhất vì việc tạo các quy trình mới là một hoạt động tương đối tốn kém.
trừ

1

Thành thật basenamedirnamegiải pháp dễ dàng hơn, nhưng bạn cũng có thể kiểm tra điều này:

find . -type f | grep -oP "[^/]*$"

hoặc là

find . -type f | rev | cut -d '/' -f1 | rev

hoặc là

find . -type f | sed "s/.*\///"

0

Như những người khác đã chỉ ra, bạn có thể kết hợp findbasename, nhưng theo mặc định basenamechương trình sẽ chỉ hoạt động trên một con đường tại một thời điểm, do đó thực thi sẽ phải được đưa ra một lần cho mỗi con đường (sử dụng một trong hai find ... -exechoặc find ... | xargs -n 1), có thể có khả năng bị chậm.

Nếu bạn sử dụng -atùy chọn trên basename, thì nó có thể chấp nhận nhiều tên tệp trong một lệnh gọi duy nhất, điều đó có nghĩa là bạn có thể sử dụng xargsmà không cần -n 1, để nhóm các đường dẫn lại với nhau thành một số lượng yêu cầu nhỏ hơn basename, sẽ hiệu quả hơn.

Thí dụ:

find /dir1 -type f -print0 | xargs -0 basename -a

Ở đây tôi đã bao gồm -print0-0(nên được sử dụng cùng nhau), để đối phó với bất kỳ khoảng trắng nào bên trong tên của tệp và thư mục.

Đây là một so sánh thời gian, giữa xargs basename -axargs -n1 basenamecác phiên bản. (Để so sánh giống như so sánh, thời gian được báo cáo ở đây là sau khi chạy giả ban đầu, để cả hai được thực hiện sau khi siêu dữ liệu tệp đã được sao chép vào bộ đệm I / O.) cksumtrong cả hai trường hợp, chỉ để chứng minh rằng đầu ra độc lập với phương thức được sử dụng.

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

Như bạn có thể thấy, nó thực sự nhanh hơn đáng kể để tránh khởi chạy basenamemọi lúc.


Đọc câu trả lời từ @minusf kỹ hơn, tôi thấy rằng trên máy Mac basenamesẽ chấp nhận nhiều tên tệp mà không cần bất kỳ đối số dòng lệnh bổ sung nào. Việc sử dụng -aở đây là trên Linux. ( basename --versionnói với tôi basename (GNU coreutils) 8.28.)
alaniwi

-3

Tôi đã tìm thấy một giải pháp (trên trang makandracards), chỉ cung cấp tên tệp mới nhất:

ls -1tr * | tail -1

(cảm ơn đến Arne Hartherz)

Tôi đã sử dụng nó cho cp:

cp $(ls -1tr * | tail -1) /tmp/

2
Điều này không trả lời câu hỏi nào cả.
kenorb
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.