Wyklucz katalogi w wyszukiwaniu lokalizacji

14

Wyszukiwanie ze locateznalezieniem ścieżek w systemie plików.
Często znasz a priori, że interesują Cię tylko pliki lub tylko katalogi.
Wyszukiwanie „zlokalizuj” często zwraca wiele wyników. Przydałoby się zawrzeć tylko jeden z typów w wyniku, ponieważ pomaga to skrócić wynik.

Istnieje jednak bardziej interesujący argument, aby pominąć pliki lub katalogi: ponieważ lista ścieżek wyników może być niejednoznaczna - nie tylko w teorii.

Poniższy przykład jest przypadkiem z prawdziwego świata i nie jest niczym niezwykłym:

$ locate --regex --basename "xfce4-keyboard-overlay$"
/usr/local/bin/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay

Ok, znaleźliśmy coś! Ale ... pliki lub katalogi?

$ file /usr/local/bin/xfce4-keyboard-overlay 
/usr/local/bin/xfce4-keyboard-overlay:   bash script

To jest plik ...

$ file /usr/local/share/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay: directory

podczas gdy drugi nie jest.

Ta dwuznaczność sprawia, że ​​długie listy ścieżek są trudne do odczytania, więc byłoby naprawdę miło odfiltrować katalogi, na przykład używając opcji linii komend dla locate.

Czy coś takiego istnieje? Nawet jeśli filtr katalogów jest inny niż lokalizacja?

Przynajmniej można użyć skryptu do iteracji wszystkich nazw plików w celu sprawdzenia - co może być powolne.

Volker Siegel
źródło

Odpowiedzi:

3

Z zsh:

print -rl ${(0)^"$(locate -0 ...)"}(N.)

(0)jest flagą rozwijania parametrów, która dzieli się na znaki NUL (jak używamy locate -0), skrót od (ps:\0:).

Dzięki ^, zamiast dodawać (N.)na końcu tablicy, dodajemy go do każdego elementu.

(N.)jest kwalifikatorem globalnym, .który pasuje tylko do zwykłych plików, Naby usunąć element, jeśli nie pasuje (nie istnieje lub nie jest zwykłym plikiem, lub nie możemy tego sprawdzić). Możesz także użyć ^/zamiast .dopasowywać nie-katalogi zamiast zwykłych plików.

print -rlwypisuje każdy argument na surowo w osobnej linii .

Możesz użyć dowolnego zshkwalifikatora globu, ale pamiętaj, że te zamawiające nie przyniosą żadnego efektu, ponieważ rozwijamy tutaj jeden glob na plik, więc dla każdego z nich jest tylko jeden plik.

(zwróć uwagę, że może się nie powieść, jeśli ostatni plik zgłoszony przez locatekończy się znakami nowej linii (błędne podstawienie polecenia we wszystkich powłokach)).

Stéphane Chazelas
źródło
3

Jest to mniej więcej tak nieeleganckie jak inne odpowiedzi, ale może mniej nieefektywne:

locate --regex --basename "xfce4-keyboard-overlay$" | 
        while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done

(podzielone na dwie linie dla czytelności). Powyższe będzie obsługiwać nazwy zawierające spacje. IFS=Wydaje się być konieczne do obsługi nazw ze spływu przestrzeni, i, oczywiście, -rpozwala obsłużyć backslashy.

Podejście „wpakuj się locatew coś” może być skazane na niepowodzenie, jeśli obecne będą ścieżki zawierające znaki nowej linii.


Aby uzyskać więcej informacji na temat IFS, przeczytaj sh(1)lub bash(1) (wpisując man shlub man bashw systemie * nix i / lub czytając go tutaj , tutaj , tutaj i / lub tutaj ). Następnie przeczytaj Zrozumienie IFS i Bash: czytaj wiersz po wierszu, z IFS na Stack Exchange (skup się na odpowiedzi z więcej niż 5 głosami), a jeśli nadal nie masz dość, sprawdź IFS na Wiki Grega i wynikach wyszukiwania IFS na Wiki Bash Hackers (nie na Stack Exchange).

G-Man mówi „Przywróć Monikę”
źródło
czy możesz dodać jakieś informacje o tym, co robi „IFS =” po whilewyciągu?
Robert
Zrobiłem to.
G-Man mówi „Przywróć Monikę”
ukośniki odwrotne nadal będą stanowić problem w przypadku wielu implementacji echa. Powinieneś używać printfdo dowolnych danych .
Stéphane Chazelas
może być rozwiązanie problemu z twoimi nowymi liniami, używając parametru „--null” locatei rozszerzając twój, readjak sugerowano tutaj transnum.blogspot.ie/2008/11/…
robert
@ StéphaneChazelas: Dobra uwaga. Naprawiony.
G-Man mówi „Przywróć Monikę”
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
źródło
W rzeczywistości jest jeszcze bardziej brudny, niż się wydaje ... ale dobra inspekcja. Udawajmy, że to pseudokod, a potem jest pomocny :)
Volker Siegel
1
@Volker: Zgadzam się, że to źle: wyświetli listę /usr/local/share/xfce4-keyboard-overlay i każdy jej podkatalog , w twoim przykładzie. Dodawanie -maxdepth 0pomaga.
G-Man mówi „Przywróć Monikę”
Idzie jeszcze lepiej ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Używanie xargsz findbyło dobrym pomysłem, edytowałem je, aby było solidne. Mam nadzieję, że nie masz nic przeciwko.
Stéphane Chazelas
1

xargspowtórzy polecenie dla każdej linii, jeśli podasz -L 1lub -iparametr.

Spójrz tutaj

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")'

Wprawdzie jest to kopanie nowej powłoki dla każdego pliku, ale ma tę zaletę, że jest ładne i kompaktowe.

EDYCJA: Nie byłam zadowolona z tej odpowiedzi, ponieważ kopała nową powłokę dla każdego pliku. To powinno mieć tylko dwa procesy:

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i echo 'test -d "{}" && echo "{}"' | bash

Oczywiście byłoby miło, gdybyśmy mogli całkowicie uniknąć wyrzucenia tłumacza, ale xargswydaje się, że ma ograniczoną zdolność do łączenia poleceń.

Robert
źródło
3
Ten właśnie zrestartował moją maszynę (był plik o nazwie /home/evil/$(reboot)/xfce4-keyboard-overlayi głupio go uruchomiłem jako root).
Stéphane Chazelas
2
@ StéphaneChazelas +1 za odwagę uruchomienia „losowego kodu z internetów” jako root;) (scnr)
Volker Siegel
0

Moje dwa centy:

while IFS= read i; \
do \
  if [ -f "$i" ]; \
  then \
    echo "$i"; \
  fi; \
done < <(locate --regex --basename "xfce4-keyboard-overlay$")

Jest to mniej więcej sposób, w jaki G-Man to zrobił w połączeniu z substytucją procesu.

Tristan Storch
źródło
Właściwie jest to mniej więcej sposób, w jaki to zrobiłem, w połączeniu z podstawieniem procesu, pomniejszonym o zdolność do obsługi nazw plików zawierających odwrotne ukośniki lub mających końcowe białe znaki. Zauważ też, że tytuł pytania brzmi „wyklucz katalogi”, a ta odpowiedź obejmuje tylko katalogi.
G-Man mówi „Przywróć Monikę”
Przepraszam. Mój błąd. Poprawione
Tristan Storch
-1

Co jeśli połączysz locatez filei grep? ...

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
petry
źródło
Nie testowałem, ale myślę, że może to być powolne, ponieważ tworzy proces filedla każdej ścieżki. Pamiętaj, że często jest wiele wierszy wyników dla lokalizacji. Mój obecny test polega na wyszukiwaniu „gnome”, podając około 73000 ścieżek do przetestowania.
Volker Siegel
2
@Volker: Gorzej: każdy $fplik to fileprogram, który otworzy ten plik i odczyta z niego . Jest to rażąco kosztowne, gdy wszystko, co musisz zrobić, to stat(). ………… To również da nieprawidłowe wyniki dla plików zawierających w nazwach „katalog” (np. „Katalog_telefoniczny”). …………… (Również for f in `…`; do …składnia nie obsługuje nazw zawierających spacje.)
G-Man mówi „Przywróć Monikę”