Jak używać polecenia znajdź, aby wyświetlić nazwy plików, ale nie ścieżkę?

Jan 06 2021

Sprawdziłem niektóre odpowiedzi online, ale nie działają, gdy -namew poleceniu jest parametr (może robię coś źle, nie jestem pewien). To, co chciałbym zrobić, to wymienić tylko nazwy plików (nie katalog / ścieżkę), a żeby jeszcze bardziej skomplikować, mam tę samą nazwę pliku z innym rozszerzeniem, więc muszę przefiltrować wynik za pomocą -namei -o, problemem są sugestie, że ja widziałem użycie albo -printflub basename, a te nie działają dobrze, gdy jest -name *.txt -o name *.pdf. Czy istnieje inny sposób rozwiązania tego problemu?

Przykładowe moje pliki:

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

Interesuje mnie tylko wystawianie jednego lub więcej typów plików na listę (używając -namedo filtrowania wyników.

Czy można to zrobić za pomocą tylko 1 findpolecenia, czy muszę to zrobić w 2 krokach (zapisanie wyniku w pliku tymczasowym, następnie przefiltrowanie / usunięcie katalogu z pliku tymczasowego)? Rezultatem, który próbuję osiągnąć, jest plik tekstowy z nazwą pliku w każdym wierszu:

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

Polecenie, którego użyłem, to

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

Używam wyszukiwania GNU.

Aktualizacja

Okazało się, że muszę umieścić \(...\), o czym przypomniał αғsнιη

Odpowiedzi

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

Używając wielu wyrażeń w trybie logicznym OR -o, musisz je wszystkie zamknąć wewnątrz \( ... \), w przeciwnym razie ostatni wynik wyrażenia jest zawsze uwzględniany; i -printf '%f\n'służy do drukowania nazw plików tylko z usuniętymi wiodącymi katalogami.

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

patrz man findw sekcji OPERATORZY o ( expr ):

OPERATORZY
...

(wyrażenie)
Wymuś pierwszeństwo. Ponieważ nawiasy są specjalne dla powłoki, zwykle trzeba je zacytować.

więc możesz użyć \( ... \)lub '(' ... ')'(białe spacje po i przed nawiasami są ważne), aby je uciec.

1 Kusalananda Jan 06 2021 at 22:58

Jak już wspomniano, -nametesty należy pogrupować w nawiasach. Te nawiasy muszą być chronione \( ... \)lub cytowane jako np. '(' ... ')'Aby chronić je przed powłoką (gdzie w innym przypadku oznaczałyby podpowłokę).

Używanie standardu find, a także nieco łatwiejsza obsługa sufiksów nazw plików:

set -- txt pdf ods

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

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

Najpierw ustawia parametry pozycyjne na liście przyrostków nazw plików, które chcielibyśmy zobaczyć (może to być również wzięte z wiersza poleceń, jeśli jest to skrypt) i tworzy listę OR -nametestów. Jest to następnie używane w findlinii poleceń.

Samo findpolecenie wywołuje następnie basenamenarzędzie dla każdej znalezionej nazwy.

Alternatywnie moglibyśmy zamiast tego użyć substytucji parametrów, aby usunąć wszystkie składniki każdej ścieżki oprócz nazwy pliku, ale w tym celu musimy przekazać nazwy ścieżek do sh -cskryptu wbudowanego :

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

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

Spodziewałbym się, że będzie to działać szybciej niż wersja używająca basename, przynajmniej jeśli jest wiele nazw plików do obsłużenia, chociaż w przypadku GNU basenamez coreutils możesz użyć -alub --multiplepolubić:

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

To zadzwoniłoby basenametak kilka razy, jak to możliwe. Ale z drugiej strony, gdybyś miał zainstalowany coreutils, możesz mieć również findutils z GNU findi mógłbyś użyć -printf '%f\n'do tego samego.