Kiedy find
widzę wszystkie pliki pdf w /home
katalogu, widzę access denied
. Aby je wyeliminować, próbowałem:
find /home -iname "*.pdf" | grep -v "access denied"
Jednak wynik jest taki sam. Jak mogę pozbyć się tych linii?
command-line
bash
find
solfish
źródło
źródło
Odpowiedzi:
To, co próbowałeś, nie zadziałało, ponieważ dane
access denied
wyjściowe są błędami i są wysyłane do STDERR zamiast do STDOUT, do którego są przesyłane potokigrep
.Możesz uniknąć oglądania tych błędów, przekierowując tylko STDERR
Lub, jak skomentował David Foerster , możemy bardziej zwięźle zamknąć STDERR
Podejrzewam jednak, że faktycznie chcesz przeszukiwać dom, a nie innych użytkowników, więc być może naprawdę tego chcesz
Jeśli spowoduje to błędy, w konfiguracji lokalnej mogą występować nieprawidłowe własności, które należy zbadać.
źródło
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. GNU find nie zakończy się, jeśli spróbuje zapisać komunikaty o błędach w dysfunkcyjnym deskryptorze pliku. Kwestie własnościsudo chown -R $USER: ...
byłyby bardziej skuteczne w przypadku większej liczby plików, które nie są własnością$USER
.Odmowa dostępu jest prawdopodobnie drukowana,
stderr
a niestdout
.Spróbuj tego:
2>&1
Przekierowuje wyjście zstderr
celustdout
, dzięki czemugrep -v
może wykonywać swoje zadania. (Domyślnie|
tylko potoki,stdout
a niestderr
.)źródło
2>&1
... Nie jestem ekspertem od bash, więc jeśli to nieprawda, proszę powiedz tak :)bash
w Ubuntu ma to, z wyjątkiem trybu POSIX . Myślę, że to najlepsze rozwiązanie - plik o złośliwej nazwieaccess denied
nadal będzie się pojawiał.Prawdopodobnie masz na myśli „Odmowa
find
dostępu ” - co pokazuje Ubuntu, gdy nie możesz uzyskać dostępu do czegoś z powodu uprawnień do plików - zamiast „odmowa dostępu”.Jedno w pełni ogólne polecenie, które wykonuje to poprawnie (i jako bonus, jest przenośne dla innych * nix es, o ile komunikat błędu jest taki sam):
(Zwykle chcesz przekazać kilka argumentów
find
. Te idą przed pierwszym przekierowaniem3>&1
).Jednak często będziesz w stanie użyć czegoś prostszego. Na przykład prawdopodobnie możesz użyć podstawienia procesu . Szczegóły poniżej.
Najczęstsze metody i ich ograniczenia
Dwa typowe podejścia to wyrzucenie stderr (jak w odpowiedzi Zanny ) lub przekierowanie stderr na stdout i filtrowanie stdout (jak w odpowiedzi Android Dev ). Chociaż mają tę zaletę, że są łatwe do napisania i często są rozsądnym wyborem, te podejścia nie są idealne.
Odrzucenie wszystkiego wysłanego do stderr - na przykład poprzez przekierowanie go do urządzenia zerowego za pomocą
2>/dev/null
lub przez zamknięcie -2>&-
powoduje ryzyko pominięcia błędów innych niż „Odmowa zezwolenia”.„Odmowa uprawnień” jest prawdopodobnie najczęstszym błędem występującym podczas działania
find
, ale jest daleka od jedynego możliwego błędu, a jeśli wystąpi inny, możesz chcieć o tym wiedzieć. W szczególnościfind
zgłasza „Brak takiego pliku lub katalogu”, jeśli punkt początkowy nie istnieje. Przy wielu punktach początkowychfind
może nadal zwracać przydatne wyniki i wydawać się działać. Na przykład, jeślia
ic
istnieje, aleb
nie istnieje ,find a b c -name x
wypisuje wynikia
, następnie „Brak takiego pliku lub katalogu” dlab
, a następnie powodujec
.Łączenie stdout i stderr razem w stdout i przesyłanie potokowe do
grep
lub inne polecenie do filtrowania go - tak jak z2>&1 | grep ...
lub -|& grep ...
powoduje ryzyko niezamierzonego odfiltrowania pliku, którego nazwa zawiera filtrowaną wiadomość.Na przykład, jeśli odfiltrujesz wiersze zawierające „Odmowa zezwolenia”, upuścisz również wyniki wyszukiwania zawierające nazwy plików, takie jak „Odmowa uprawnień messages.txt”. Prawdopodobnie stało się tak przez przypadek, ale możliwe byłoby również nadanie plikowi specjalnie spreparowanej nazwy w celu uniemożliwienia wyszukiwania.
Filtrowanie połączonych strumieni wiąże się z innym problemem, którego nie można złagodzić poprzez bardziej selektywne filtrowanie (na przykład
grep -vx 'find: .*: Permission denied'
po prawej stronie rury). Niektórefind
akcje, w tym-print
akcja domyślna, gdy nie zostanie podana żadna akcja, określają sposób wyświetlania nazw plików na podstawie tego, czy standardowe wyjście jest terminalem, czy nie .?
zamiast tego są drukowane.grep
) powoduje, żefind
nie widać już terminala. (Dokładniej, powoduje, że jego standardowe wyjście nie jest terminalem.) Następnie dziwne znaki są wypisywane dosłownie. Ale jeśli wszystkie polecenia po prawej stronie potoku to (a) usuń wiersze, które wyglądają jak komunikaty „Odmowa zezwolenia” i (b) wydrukuj to, co pozostało, to nadal podlegasz rodzajowi shenanigans, któryfind
jest terminalem wykrywanie ma na celu zapobieganie.man find
uzyskać więcej informacji, zobacz sekcję NIEZWYKŁE PLIKI , w tym zachowanie każdej z akcji drukujących nazwy plików. ( „Wiele akcji find powoduje wydruk danych, które są kontrolowane przez innych użytkowników ...” ) Patrz także sekcje 3.3.2.1 , 3.3.2.2 i 3.3.2.3 podręcznika GNU Findutils .Powyższe omówienie nietypowych nazw plików dotyczy znalezienia GNU , czyli
find
implementacji w systemach GNU / Linux, w tym Ubuntu.Pozostawienie standardowego wyjścia w spokoju podczas filtrowania błędu standardowego
Czego naprawdę chcą tu jest pozostawienie stdout nienaruszone podczas przekierowywania stderr do
grep
. Niestety nie ma na to prostej składni.|
potoki stdout, a niektóre powłoki (w tymbash
) obsługują|&
potokowanie obu strumieni - lub możesz najpierw przekierować stderr na stdout2>&1 |
, co ma ten sam efekt. Ale powszechnie używane powłoki nie zapewniają składni tylko dla stderr potoku.Nadal możesz to zrobić. To po prostu niezręczne. Jednym ze sposobów jest zamiana stdout na stderr , aby wyniki wyszukiwania były ustawione na stderr, a błędy na stdout, a następnie stdout rurki do
grep
filtrowania:Zwykle przekazujesz argumenty
find
, takie jak punkty początkowe (miejsca wyszukiwania, które zwykle są katalogami) i predykaty (testy i działania). Te idą w miejsceargs
wyżej.Działa to poprzez wprowadzenie nowego deskryptora pliku w celu utrzymania jednego z dwóch standardowych strumieni, które chcesz zamienić, wykonanie przekierowań w celu zamiany i zamknięcie nowego deskryptora pliku.
3>&1
przekierowuje deskryptor pliku 3 na stdout, tak że gdy stdout (deskryptor pliku 1) jest następnie przekierowywany, oryginalny stdout nadal można łatwo zapisać.1>&2
przekierowuje stdout na stderr. Ponieważ deskryptor pliku 3 jest nadal oryginalnym standardowym wyjściem, nadal można uzyskać do niego dostęp.2>&3
przekierowuje stderr do deskryptora pliku 3, który jest oryginalnym stdout.3>&-
zamyka deskryptor pliku 3, który nie jest już potrzebny.Jednak ta metoda ma tę wadę, że wyniki wyszukiwania są wysyłane do stderr, a błędy są wysyłane do stdout . Jeśli uruchamiasz to polecenie bezpośrednio w interaktywnej powłoce, a nie przesyłasz danych wyjściowych ani nie przekierowujesz ich dalej, to nie ma to większego znaczenia. W przeciwnym razie może to stanowić problem. Jeśli umieścisz to polecenie w skrypcie, a następnie ktoś (być może ty później) przekieruje lub potokuje jego dane wyjściowe, nie będzie działał zgodnie z oczekiwaniami .
Rozwiązaniem jest zamiana strumieni po zakończeniu filtrowania danych wyjściowych . Zastosowanie tych samych przekierowań pokazanych powyżej po prawej stronie potoku nie osiągnie tego, ponieważ
|
tylko stdout potoków, tak że strona potoku odbiera tylko dane wyjściowe, które zostały pierwotnie wysłane do stderr (ponieważ strumienie zostały zamienione), a nie oryginalne wyjście standardowe. Zamiast tego można użyć(
)
powyższej komendy w podpowłoce ( powiązanej ), a następnie zastosować do niej przekierowania wymiany:Działa to grupowanie, a nie podpowłoka. Jeśli wolisz, możesz użyć
{
;}
:Mniej kłopotliwy sposób: proces zastępowania
Niektóre powłoki, w tym Bash na systemach, które mogą je obsługiwać (w tym systemach GNU / Linux, takich jak Ubuntu), pozwalają na wykonanie procesu podstawiania , co pozwala na uruchomienie polecenia i przekierowanie do / z jednego ze strumieni. Możesz przekierować
find
stderrgrep
polecenia na polecenie, które je filtruje, i przekierowaćgrep
stdout tego polecenia na stderr.Podziękowania dla Android Dev za ten pomysł.
1>&2
. Użyłem>&2
, co jest równoważne ( powiązane ). Używaj, co chcesz.Chociaż
bash
obsługuje podstawianie procesów,sh
w Ubuntu jestdash
, co nie. Otrzymasz komunikat „Błąd składni: nieoczekiwane przekierowanie”, jeśli spróbujesz użyć tej metody, podczas gdy metoda zamiany stdout i stderr nadal będzie działać. Ponadto, gdybash
działa w trybie POSIX , obsługa zastępowania procesów jest wyłączona.Jedną z sytuacji, w której
bash
działa w trybie POSIX, jest wywołanie go jakosh
1 . Dlatego w systemie operacyjnym takim jak Fedora, gdziebash
jest to możliwe/bin/sh
, lub jeśli na Ubuntu/bin/sh
ustawiłeś symboliczne łącze dobash
siebie, podstawianie procesów nadal nie działa wsh
skrypcie, bez wcześniejszego polecenia wyłączenia trybu POSIX. Najlepszym rozwiązaniem, jeśli chcesz użyć tej metody w skrypcie, jest umieszczenie#!/bin/bash
na górze zamiast#!/bin/sh
, jeśli jeszcze tego nie zrobiłeś.1 : W tej sytuacji
bash
automatycznie włącza tryb POSIX po uruchomieniu poleceń w skryptach startowych.Przykład
Warto przetestować te polecenia. Aby to zrobić, tworzę
tmp
podkatalog bieżącego katalogu i zapełniam go niektórymi plikami i katalogami, zabierając uprawnienia jednemu z nich, aby wywołać błąd „Odmowa zezwolenia”find
.Jednym z katalogów, które jest dostępne zawiera plik „Permission denied” w nazwie. Uruchamianie
find
bez przekierowania lub rur pokazuje ten plik, ale również pokazuje rzeczywiste „Permission denied” błąd do innego katalogu, który nie jest dostępny:Przesłanie zarówno stdout, jak i stderr do
grep
i odfiltrowanie wierszy zawierających „Odmowa zezwolenia” powoduje zniknięcie komunikatu o błędzie, ale także ukrywa wynik wyszukiwania pliku z tą frazą w nazwie:find 2>&1 | grep -Fv 'Permission denied'
jest równoważny i daje taką samą wydajność.Pokazane powyżej metody odfiltrowywania „Odmowy uprawnień” tylko z komunikatów o błędach - a nie z wyników wyszukiwania - są skuteczne. Na przykład oto metoda zamiany stdout i stderr:
find args 2> >(grep -Fv 'Permission denied' >&2)
daje taką samą wydajność.Możesz wywołać inny komunikat o błędzie, aby upewnić się, że linie wysyłane do stderr, które nie zawierają tekstu „Odmowa zezwolenia” są nadal dozwolone. Na przykład tutaj uruchomiłem
find
z bieżącym katalogiem (.
) jako jednym punktem początkowym, ale nieistniejącym katalogiemfoo
jako innym:Sprawdzanie, czy
find
standardowe wyjście jest nadal terminalemMożemy również zobaczyć, które polecenia powodują, że znaki specjalne, takie jak znak nowej linii, są wyświetlane dosłownie. (Można to zrobić oddzielnie od powyższej demonstracji i nie musi znajdować się w
tmp
katalogu).Utwórz plik z nową linią w nazwie:
Zwykle używamy katalogów jako punktów wyjścia
find
, ale pliki też działają:Piping stdout do innego polecenia powoduje, że nowy wiersz jest wypisywany dosłownie, tworząc fałszywe wrażenie dwóch oddzielnych wyników wyszukiwania
abc
idef
. Możemy to przetestować za pomocącat
:Przekierowanie tylko stderr nie powoduje tego problemu:
Ani też go nie zamyka:
Rurociągów do
grep
robi przyczyną problemu:(Zastąpienie
|&
przez2>&1 |
jest równoważne i daje takie same wyniki.)Zamiana stdout i stderr i orurowanie stdout nie powoduje problemu
find
- stdout staje się stderr, który nie jest orurowany :Grupowanie tego polecenia i zamiana strumieni z powrotem nie powoduje problemu:
(
{
;}
Wersja daje takie same dane wyjściowe.)Zastosowanie substytucji procesu do filtrowania stderr nie powoduje problemu:
źródło