Bagaimana cara menggunakan perintah find untuk mencantumkan nama file tetapi bukan jalurnya?

Jan 06 2021

Saya telah memeriksa beberapa jawaban online, tetapi jawaban tersebut tidak berfungsi ketika ada -nameparameter dalam perintah (mungkin saya melakukan sesuatu yang salah, tidak yakin). Yang ingin saya lakukan adalah mencantumkan hanya nama file (bukan direktori / jalur), dan yang lebih rumit lagi, saya memiliki nama file yang sama dengan ekstensi yang berbeda, jadi saya perlu memfilter hasilnya menggunakan -namedan -o, masalahnya adalah saran yang saya berikan. melihat menggunakan salah satu -printfatau basename, dan itu tidak bekerja dengan baik bila ada -name *.txt -o name *.pdf. Apakah ada cara lain untuk mengatasi masalah ini?

Contoh file saya:

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

Saya hanya tertarik untuk mendaftar satu atau lebih jenis file per daftar (dengan menggunakan -nameuntuk memfilter hasil saya.

Apakah mungkin untuk melakukannya dengan hanya menggunakan 1 findperintah atau saya harus melakukannya dalam 2 langkah (menyimpan hasilnya dalam file sementara, lalu filter / strip direktori dari file temp)? Hasil yang saya coba capai adalah file teks dengan nama file per baris:

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

Perintah yang saya gunakan adalah

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

Saya menggunakan GNU find.

Memperbarui

Ternyata saya harus meletakkan \(...\), seperti yang diingatkan oleh αғsнιη

Jawaban

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

Saat menggunakan beberapa ekspresi dalam mode OR logis dengan -oAnda perlu menyertakan semuanya di dalamnya \( ... \), jika tidak, hasil ekspresi terakhir selalu dihormati; dan -printf '%f\n'digunakan untuk mencetak nama file hanya dengan direktori utama yang dihapus.

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

lihat man finddi bagian OPERATOR tentang ( expr ):

OPERATOR
...

(expr)
Paksa prioritas. Karena tanda kurung khusus untuk shell, biasanya Anda perlu mengutipnya.

jadi Anda bisa menggunakan \( ... \)atau '(' ... ')'(spasi setelah dan sebelum tanda kurung penting) untuk menghindarinya.

1 Kusalananda Jan 06 2021 at 22:58

Seperti yang telah disebutkan, Anda perlu mengelompokkan -namepengujian Anda dalam tanda kurung. Tanda kurung ini perlu di-escape sebagai \( ... \)atau dikutip sebagai misalnya '(' ... ')'untuk melindunginya dari shell (jika tidak, tanda kurung akan menunjukkan subkulit).

Menggunakan standar find, dan juga membuat penanganan sufiks nama file sedikit lebih mudah:

set -- txt pdf ods

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

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

Ini pertama-tama menetapkan parameter posisi ke daftar sufiks nama file yang ingin kita lihat (ini juga dapat diambil dari baris perintah jika ini adalah skrip), dan menyusun daftar OR -nametes. Ini kemudian digunakan di findbaris perintah.

The para findperintah itu sendiri kemudian memanggil basenameutilitas untuk setiap nama ditemukan.

Sebagai alternatif, kita dapat menggunakan substitusi parameter untuk menghapus semua kecuali komponen nama file dari setiap nama jalur, tetapi untuk melakukannya kita harus meneruskan nama jalur ke sh -cskrip sebaris :

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

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

Saya berharap ini berjalan lebih cepat daripada variasi yang digunakan basename, setidaknya jika ada banyak nama file yang harus ditangani, meskipun dengan GNU basenamedari coreutils, Anda dapat menggunakan -aatau --multiplemenyukainya:

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

Ini akan menelepon basenamesesingkat mungkin. Tetapi sekali lagi, jika Anda menginstal coreutils, Anda mungkin juga memiliki findutils, dengan GNU find, dan dapat menggunakannya -printf '%f\n'untuk melakukan hal yang sama.