Bagaimana cara mendapatkan nama file dengan Linux 'find'?

266

Saya menggunakan find ke semua file dalam direktori, jadi saya mendapatkan daftar path. Namun, saya hanya perlu nama file. yaitu saya dapatkan ./dir1/dir2/file.txtdan saya ingin mendapatkannyafile.txt

marverix
sumber

Jawaban:

359

Di GNU findAnda dapat menggunakan -printfparameter untuk itu, misalnya:

find /dir1 -type f -printf "%f\n"
SiegeX
sumber
9
Jelas jawabannya , tetapi kurang detail.
Jason McCreary
25
ini hanya untuk GNU
Osama F Elias
3
Ini tidak berfungsi untuk saya ketika saya menggunakan beberapa jenis file (-o switch)
Urchin
9
find: -printf: primer atau operator tidak dikenal
holms
@Urchin Tidak ada alasan tidak semestinya selama Anda memiliki logika yang benar (yaitu -omemiliki prioritas lebih rendah daripada yang tersirat -a, sehingga Anda akan sering ingin mengelompokkan -oargumen Anda )
Reinstate Monica Silakan
157

Jika temuan Anda tidak memiliki opsi -printf, Anda juga dapat menggunakan nama samaran:

find ./dir1 -type f -exec basename {} \;
Kambus
sumber
17
Mengutipkan titik koma adalah cara lain untuk ... {} ';'
mendamaikan
28

Gunakan -execdiryang secara otomatis menyimpan file saat ini {}, misalnya:

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

Anda juga dapat menggunakan $PWDsebagai gantinya .(pada beberapa sistem itu tidak akan menghasilkan titik ekstra di depan).

Jika Anda masih mendapat titik tambahan, Anda juga dapat menjalankan:

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

-execdir utility [argument ...] ;

The -execdirutama adalah identik dengan -execprimer dengan pengecualian bahwa utilitas akan dieksekusi dari direktori yang memegang file saat ini .

Saat digunakan +sebagai pengganti ;, kemudian {}diganti dengan sebanyak mungkin nama path untuk setiap pemanggilan utilitas. Dengan kata lain, itu akan mencetak semua nama file dalam satu baris.

kenorb
sumber
Saya mendapatkan ./filenamebukan filename. Tergantung pada kebutuhan Anda, mungkin atau mungkin tidak baik-baik saja.
user276648
@ user276648 Coba dengan $PWDbukan ..
kenorb
24

Jika Anda menggunakan GNU find

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

Atau Anda dapat menggunakan bahasa pemrograman seperti Ruby (1.9+)

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

Jika Anda suka solusi bash (setidaknya 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done
kurumi
sumber
Saya terinspirasi oleh jawaban Anda untuk membantu sleske: serverfault.com/a/745968/329412
Nolwennig
11

Jika Anda ingin menjalankan beberapa tindakan terhadap nama file saja, menggunakan basenamebisa jadi sulit.

Sebagai contoh ini:

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

hanya akan mengulang nama dasar /my/found/path. Bukan yang kita inginkan jika kita ingin mengeksekusi pada nama file.

Tetapi Anda bisa kemudian xargsoutput. misalnya untuk membunuh file dalam direktori berdasarkan nama di direktori lain:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
j03m
sumber
jangan gema -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike
7

Pada mac (BSD find) gunakan:

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

-execdan -execdirlambat, xargsadalah raja.

$ 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

xargsParalelisme juga membantu.

Lucunya saya tidak bisa menjelaskan kasus terakhir xargstanpa -n1. Ini memberikan hasil yang benar dan tercepat¯\_(ツ)_/¯

( basenamehanya membutuhkan 1 argumen jalan tetapi xargsakan mengirim semuanya (sebenarnya 5000) tanpa -n1. Tidak berfungsi di linux dan openbsd, hanya macOS ...)

Beberapa angka yang lebih besar dari sistem linux untuk melihat bagaimana -execdirmembantu, tetapi masih jauh lebih lambat daripada paralel 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
minusf
sumber
1
hanya satu titik data lagi: pada openbsd dalam jangka waktu yang lama findadalah -execdiryang menjadi tercepat karena menciptakan proses baru adalah operasi yang relatif mahal.
minusf
1

Jujur basenamedan dirnamesolusinya lebih mudah, tetapi Anda juga dapat memeriksanya:

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

atau

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

atau

find . -type f | sed "s/.*\///"
Warga kehormatan
sumber
0

Seperti yang telah ditunjukkan orang lain, Anda dapat menggabungkan finddan basename, tetapi secara default basenameprogram hanya akan beroperasi pada satu jalur pada satu waktu, sehingga yang dapat dieksekusi harus diluncurkan satu kali untuk setiap jalur (menggunakan salah satu find ... -execatau find ... | xargs -n 1), yang mungkin berpotensi lambat.

Jika Anda menggunakan -aopsi aktif basename, maka ia dapat menerima beberapa nama file dalam satu pemanggilan tunggal, yang berarti bahwa Anda kemudian dapat menggunakan xargstanpa -n 1, untuk mengelompokkan jalur bersama menjadi jumlah pemanggilan yang jauh lebih kecil basename, yang seharusnya lebih efisien.

Contoh:

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

Di sini saya telah menyertakan -print0dan -0(yang harus digunakan bersama-sama), untuk mengatasi spasi putih di dalam nama file dan direktori.

Berikut ini adalah perbandingan waktu, antara versi xargs basename -adan xargs -n1 basename. (Demi perbandingan suka-dengan-suka, timing yang dilaporkan di sini adalah setelah dummy run awal, sehingga keduanya dilakukan setelah file metadata telah disalin ke cache I / O.) Saya telah mengirim output ke cksumdalam kedua kasus, hanya untuk menunjukkan bahwa output tidak tergantung pada metode yang digunakan.

$ 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

Seperti yang Anda lihat, ini sebenarnya jauh lebih cepat untuk menghindari peluncuran basenamesetiap saat.

alaniwi
sumber
Membaca jawaban dari @minusf lebih teliti, saya melihat bahwa pada Mac basenameakan menerima beberapa nama file tanpa memerlukan argumen baris perintah tambahan. Penggunaan di -asini adalah di Linux. ( basename --versionMemberitahu saya basename (GNU coreutils) 8.28.)
alaniwi
-3

Saya telah menemukan solusi (pada halaman makandracard), yang hanya memberikan nama file terbaru:

ls -1tr * | tail -1

(Terima kasih kepada Arne Hartherz)

Saya menggunakannya untuk cp:

cp $(ls -1tr * | tail -1) /tmp/
jazzinka
sumber
2
Ini sama sekali tidak menjawab pertanyaan.
kenorb