Як отримати лише ім'я файлу за допомогою "знайти" Linux?


266

Я використовую пошук для всіх файлів у каталозі, тому я отримую список шляхів. Однак мені потрібні лише імена файлів. тобто я отримую ./dir1/dir2/file.txtі хочу отриматиfile.txt

Відповіді:


359

У GNU findви можете використовувати -printfдля цього параметр, наприклад:

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

9
Однозначно відповідь , але в ній бракує деталей.
Jason McCreary

25
це лише для GNU
Osama F Elias

3
Це не працює для мене, коли я використовую кілька типів файлів (-o перемикач)
Urchin

9
знайти: -printf: невідомий основний або оператор
holms

@Urchin Немає причини це не повинні так довго , як у вас є правильна логіка (тобто -oмають більш низький пріоритет , ніж ті, що припускаються -a, так що ви будете часто хочуть , щоб згрупувати ваші -oаргументи)
відновить Моніку ласка

157

Якщо у знахідці немає опції -printf, ви також можете використовувати базове ім'я:

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

17
Цитування крапки з комою - ще один спосіб ... {} ';'
розмежування

28

Використовуйте, -execdirде автоматично зберігається поточний файл {}, наприклад:

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

Ви також можете використовувати $PWDзамість .(у деяких системах це не видасть зайвої крапки спереду).

Якщо у вас все ще є додаткова крапка, ви можете запустити:

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

-execdir utility [argument ...] ;

-execdirПервинна ідентична -execпервинний за винятком того , утиліта буде виконана з каталогу, тримає поточний файл .

Якщо використовується +замість ;, то {}замінюється якомога більше імен шляхів для кожного виклику утиліти. Іншими словами, він буде друкувати всі імена файлів в одному рядку.


Я отримую ./filenameзамість filename. Залежно від ваших потреб, це може бути, а може і не бути добре.
user276648

@ user276648 Спробуйте $PWDзамість цього ..
kenorb

24

Якщо ви використовуєте GNU find

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

Або ви можете використовувати мову програмування, наприклад, Ruby (1.9+)

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

Якщо ви хочете уявити собі башти (принаймні 4) рішення

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

Мене надихнула ваша відповідь на допомогу sleske: serverfault.com/a/745968/329412
Nolwennig

11

Якщо ви хочете виконати якусь дію лише проти імені файлу, використання basenameможе бути важким.

Наприклад це:

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

буде просто відлунювати базовим іменем /my/found/path. Не те, що ми хочемо, якщо ми хочемо виконати на ім'я файлу.

Але ви можете потім xargsвихід. наприклад, вбивати файли в dir на основі імен іншого dir:

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

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


1

-execі -execdirповільні, xargsкороль.

$ 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

xargsПаралелізм Росії також допомагає.

Як не дивно, я не можу пояснити останній випадок xargsбез -n1. Це дає правильний результат і це найшвидше¯\_(ツ)_/¯

( basenameбере лише 1 аргумент шляху, але xargsнадішле їх усі (фактично 5000) без -n1. Не працює на Linux та openbsd, лише на macOS ...)

Деякі великі числа в системі Linux, щоб побачити, як це -execdirдопомагає, але все ж набагато повільніше, ніж паралель 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
лише ще один момент даних: у openbsd у довгостроковій перспективі findце -execdirстає найшвидшим, оскільки створення нових процесів є відносно дорогою операцією.
мінус

1

Чесно basenameі dirnameрішення простіше, але ви також можете це перевірити:

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

або

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

або

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

0

Як уже зазначалося, ви можете комбінувати findі basename, але за замовчуванням basenameпрограма буде працювати тільки на одному шляху , в той час, так що виконуваний файл буде мати бути запущений один раз для кожного шляху ( з використанням або find ... -execабо find ... | xargs -n 1), які потенційно можуть бути повільним.

Якщо ви користуєтесь -aопцією увімкнено basename, то вона може приймати кілька імен файлів за один виклик, а це означає, що ви можете потім використовувати xargsбез -n 1, щоб згрупувати шляхи разом у набагато меншу кількість викликів basename, яка повинна бути більш ефективною.

Приклад:

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

Тут я включив -print0і -0(які слід використовувати разом), щоб впоратися з будь-яким пробілом всередині імен файлів та каталогів.

Ось порівняння часу між версіями xargs basename -aта xargs -n1 basename. (Для порівняння, подібного до подібного, тимчасові звіти, які тут повідомляються, проходять після початкового запуску манекена, так що вони виконуються після того, як метадані файлу вже скопійовані в кеш вводу / виводу.) Я переклав вихід на cksumв обох випадках просто для того, щоб продемонструвати, що результат не залежить від використовуваного методу.

$ 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

Як бачите, це дійсно значно швидше, щоб уникнути запуску basenameкожного разу.


Читаючи відповідь @minusf більш ретельно, я бачу, що на Mac basenameприйматимуть декілька імен файлів, не потребуючи додаткових аргументів командного рядка. Використання -aтут є в Linux. ( basename --versionрозповідає basename (GNU coreutils) 8.28.)
alaniwi

-3

Я знайшов рішення (на сторінці makandracards), яке дає найновіше ім'я файлу:

ls -1tr * | tail -1

(спасибі Арне Хартерцу)

Я використовував його для cp:

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

2
Це зовсім не відповідає на питання.
kenorb
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.