Jak uzyskać nazwę pliku tylko w systemie Linux „znajdź”?

266

Używam find do wszystkich plików w katalogu, więc otrzymuję listę ścieżek. Potrzebuję jednak tylko nazw plików. tzn. dostaję ./dir1/dir2/file.txti chcę dostaćfile.txt

marverix
źródło

Odpowiedzi:

359

W GNU findmożesz do tego użyć -printfparametru, np .:

find /dir1 -type f -printf "%f\n"
SiegeX
źródło
9
Oczywiście odpowiedź , ale brakuje w niej szczegółów.
Jason McCreary,
25
dotyczy tylko GNU
Osama F Elias
3
To nie działa dla mnie, gdy używam wielu typów plików (przełącznik -o)
Urchin
9
znajdź: -printf: nieznany główny lub operator
holms
@Urchin Nie ma powodu, dla którego nie powinno być tak długo, dopóki masz prawidłową logikę (tj. -oMa niższy priorytet niż sugerowany -a, więc często będziesz chciał pogrupować swoje -oargumenty)
Przywróć Monikę Proszę
157

Jeśli twoje wyszukiwanie nie ma opcji -printf, możesz także użyć basename:

find ./dir1 -type f -exec basename {} \;
Kambus
źródło
17
Cytując średnik to kolejny sposób na ... {} ';'
jednoznaczne
28

Użyj, -execdirktóra automatycznie przechowuje bieżący plik {}, na przykład:

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

Możesz także użyć $PWDzamiast .(w niektórych systemach nie będzie generować dodatkowej kropki z przodu).

Jeśli nadal masz dodatkową kropkę, możesz uruchomić:

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

-execdir utility [argument ...] ;

-execdirPodstawowy jest identyczny z -execpierwotnym z wyjątkiem tego, że narzędzie będzie wykonywany z katalogu, który posiada aktualny plik .

Jeśli zostanie użyty +zamiast ;, to {}zostanie zastąpiony jak największą liczbą ścieżek dla każdego wywołania narzędzia. Innymi słowy, wypisze wszystkie nazwy plików w jednym wierszu.

kenorb
źródło
Dostaję ./filenamezamiast filename. W zależności od potrzeb może być lub nie być w porządku.
user276648,
@ user276648 Spróbuj z $PWDzamiast ..
kenorb
24

Jeśli używasz GNU find

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

Lub możesz użyć języka programowania, takiego jak Ruby (1.9+)

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

Jeśli masz ochotę na bash (co najmniej 4) rozwiązanie

shopt -s globstar
for file in **; do echo ${file##*/}; done
kurumi
źródło
Zainspirowała mnie twoja odpowiedź, aby pomóc sleske: serverfault.com/a/745968/329412
Nolwennig
11

Jeśli chcesz uruchomić jakieś działanie tylko przeciwko nazwie pliku, użycie basenamemoże być trudne.

Na przykład to:

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

po prostu wyśle ​​echo basename /my/found/path. Nie to, co chcemy, jeśli chcemy wykonać na nazwie pliku.

Ale możesz wtedy uzyskać xargswynik. na przykład, aby zabić pliki w katalogu na podstawie nazw w innym katalogu:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm
j03m
źródło
dont echo -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike
7

Na Mac (BSD find) użyj:

find /dir1 -type f -exec basename {} \;
widmo
źródło
1

-execi -execdirsą powolne, xargsjest królem.

$ 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

xargsRównoległość pomaga również.

Co zabawne, nie potrafię wyjaśnić ostatniego przypadku xargsbez -n1. Daje poprawny wynik i jest najszybszy¯\_(ツ)_/¯

( basenamepobiera tylko 1 argument ścieżki, ale xargswyśle ​​je wszystkie (właściwie 5000) bez -n1. nie działa na systemie Linux i openbsd, tylko macOS ...)

Niektóre większe liczby z systemu Linux, aby zobaczyć, jak -execdirpomaga, ale wciąż znacznie wolniej niż równolegle 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
źródło
1
Jeszcze jeden punkt pomiarowy: w OpenBSD w długi uruchomiony findjest to -execdir, że staje się najszybciej jak tworzenie nowych procesów jest stosunkowo kosztowna operacja.
minusf
1

Szczerze basenamei dirnamerozwiązania są łatwiejsze, ale można też to sprawdzić:

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

lub

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

lub

find . -type f | sed "s/.*\///"
Obywatel
źródło
0

Jak zauważyli inni, możesz łączyć findi basename, ale domyślnie basenameprogram będzie działał tylko na jednej ścieżce na raz, więc plik wykonywalny będzie musiał zostać uruchomiony raz dla każdej ścieżki (przy użyciu jednego find ... -execlub dwóch find ... | xargs -n 1), co może być potencjalnie wolne.

Jeśli użyjesz tej -aopcji basename, może zaakceptować wiele nazw plików w jednym wywołaniu, co oznacza, że ​​możesz użyć xargsbez -n 1, aby pogrupować ścieżki w znacznie mniejszą liczbę wywołań basename, co powinno być bardziej wydajne.

Przykład:

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

Tutaj umieściłem -print0i -0(które powinny być używane razem), aby poradzić sobie z dowolnymi spacjami w nazwach plików i katalogów.

Oto porównanie czasowe między wersjami xargs basename -ai xargs -n1 basename. (Ze względu na porównanie typu „tak jak z podobnym” podane tutaj czasy są po początkowym manekinie, więc oba są wykonywane po skopiowaniu metadanych pliku do pamięci podręcznej we / wy.) Przesłałem dane wyjściowe do cksumw obu przypadkach tylko w celu wykazania, że ​​dane wyjściowe są niezależne od zastosowanej metody.

$ 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

Jak widać, naprawdę jest znacznie szybsze, aby uniknąć uruchamiania za basenamekażdym razem.

alaniwi
źródło
Dokładniej czytając odpowiedź z @minusf, widzę, że na komputerze Mac basenameakceptuje wiele nazw plików bez potrzeby stosowania dodatkowych argumentów wiersza polecenia. Wykorzystanie -atutaj jest w systemie Linux. ( basename --versionmówi mi basename (GNU coreutils) 8.28.)
alaniwi
-3

Znalazłem rozwiązanie (na stronie makandracards), które podaje tylko najnowszą nazwę pliku:

ls -1tr * | tail -1

(podziękowania dla Arne Hartherz)

Użyłem go do cp:

cp $(ls -1tr * | tail -1) /tmp/
jazzinka
źródło
2
To wcale nie odpowiada na pytanie.
kenorb