Làm thế nào để sử dụng lệnh find để liệt kê tên tệp nhưng không có đường dẫn?

Jan 06 2021

Tôi đã kiểm tra một số câu trả lời trực tuyến, nhưng chúng không hoạt động khi có một -nametham số trong lệnh (có thể tôi đang làm sai điều gì đó, không chắc chắn). Những gì tôi muốn làm là chỉ liệt kê các tên tệp (không phải thư mục / đường dẫn) và phức tạp hơn, tôi có cùng một tên tệp với phần mở rộng khác nhau, vì vậy tôi cần lọc kết quả bằng cách sử dụng -name-o, vấn đề là các đề xuất mà tôi đã thấy sử dụng hoặc -printfhoặc basename, và những thứ đó không hoạt động tốt khi có -name *.txt -o name *.pdf. Có cách nào khác để giải quyết vấn đề này không?

Ví dụ về các tệp của tôi:

/dir1/dir2/dir3/dir4/
                    /file1.txt
                    /file1.pdf
                    /file1.ods ...etc.

Tôi chỉ quan tâm đến việc liệt kê một hoặc nhiều loại tệp cho mỗi danh sách (bằng cách sử dụng -nameđể lọc kết quả của tôi.

Có thể thực hiện được nó chỉ bằng 1 findlệnh hay tôi phải thực hiện 2 bước (lưu kết quả vào tệp tạm thời, sau đó lọc / tách thư mục khỏi tệp tạm thời)? Kết quả mà tôi đang cố gắng hoàn thành là một tệp văn bản có tên tệp trên mỗi dòng:

file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.

Lệnh tôi đang sử dụng là

find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"

Tôi đang sử dụng GNU find.

Cập nhật

Hóa ra tôi phải đặt \(...\), như được nhắc bởi αғsнιη

Trả lời

3 αғsнιη Jan 06 2021 at 21:02

Khi sử dụng nhiều biểu thức trong chế độ OR logic, -obạn cần đặt tất cả chúng bên trong \( ... \), nếu không, kết quả biểu thức cuối cùng luôn được tôn trọng; và -printf '%f\n'được sử dụng để in tên tệp chỉ khi xóa bất kỳ thư mục hàng đầu nào.

find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'

thấy man finddưới OPERATORS phần về ( expr ):

ĐIỀU HÀNH
...

(expr) Quyền
ưu tiên. Vì dấu ngoặc đơn là đặc biệt đối với shell, thông thường bạn sẽ cần phải trích dẫn chúng.

vì vậy bạn có thể sử dụng \( ... \)hoặc '(' ... ')'(khoảng trắng sau và trước dấu ngoặc đơn là quan trọng) để thoát khỏi chúng.

1 Kusalananda Jan 06 2021 at 22:58

Như đã được đề cập, bạn cần phải nhóm các -namebài kiểm tra của mình trong ngoặc đơn. Các dấu ngoặc đơn này cần được thoát ra dưới dạng \( ... \)hoặc được trích dẫn như ví dụ '(' ... ')'để bảo vệ chúng khỏi shell (nơi mà chúng sẽ chỉ định một shell con).

Sử dụng tiêu chuẩn findvà cũng làm cho việc xử lý các hậu tố tên tệp dễ dàng hơn một chút:

set -- txt pdf ods

for suffix do
    set -- "$@" -o -name "*.$suffix"
    shift
done
shift

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename {} \;

Điều này đầu tiên đặt các tham số vị trí vào danh sách các hậu tố tên tệp mà chúng tôi muốn xem (điều này cũng có thể được lấy từ dòng lệnh nếu đây là một tập lệnh) và xây dựng danh sách OR của các -namebài kiểm tra. Điều này sau đó được sử dụng trong finddòng lệnh.

Càng findlệnh riêng của mình sau đó gọi các basenametiện ích cho mỗi tên được tìm thấy.

Thay vào đó, chúng ta có thể sử dụng thay thế tham số để loại bỏ tất cả trừ thành phần tên tệp của mỗi tên đường dẫn, nhưng để làm như vậy, chúng ta phải chuyển tên đường dẫn vào một sh -ctập lệnh nội tuyến :

# [...] as before [...]

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec sh -c '
    for pathname do
        printf "%s\n" "${pathname##*/}"
    done' sh {} +

Tôi hy vọng điều này sẽ chạy nhanh hơn so với biến thể bằng cách sử dụng basename, ít nhất là nếu có nhiều tên tệp cần xử lý, mặc dù với GNU basenametừ coreutils, bạn có thể sử dụng -ahoặc --multiplethích như vậy:

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +

Điều này sẽ gọi basenamecàng ít lần càng tốt. Nhưng một lần nữa, nếu bạn đã cài đặt coreutils, bạn cũng có thể có findutils, với GNU find, và có thể sử dụng -printf '%f\n'để làm điều tương tự.