Wyjęcie linii „Odmowa dostępu”

9

Kiedy findwidzę wszystkie pliki pdf w /homekatalogu, 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?

solfish
źródło

Odpowiedzi:

19

To, co próbowałeś, nie zadziałało, ponieważ dane access deniedwyjściowe są błędami i są wysyłane do STDERR zamiast do STDOUT, do którego są przesyłane potoki grep.

Możesz uniknąć oglądania tych błędów, przekierowując tylko STDERR

find /home -iname "*.pdf" 2>/dev/null

Lub, jak skomentował David Foerster , możemy bardziej zwięźle zamknąć STDERR

find /home -iname "*.pdf" 2>&-

Podejrzewam jednak, że faktycznie chcesz przeszukiwać dom, a nie innych użytkowników, więc być może naprawdę tego chcesz

find ~ -iname "*.pdf"

Jeśli spowoduje to błędy, w konfiguracji lokalnej mogą występować nieprawidłowe własności, które należy zbadać.

Zanna
źródło
3
Grrr, dlaczego ludzie zawsze biją mnie o 30 sekund? : \
You'reAGitForNotUsingGit
find: „/home/ihsan/.gvfs”: odmowa dostępu find: „/home/ihsan/.dbus”: odmowa dostępu, dla polecenia ~
solfish
czy jest w tym coś złego? tak, chcę również do katalogu domowego innych użytkowników, który również jest tworzony przeze mnie do testowania
solfish
2
@solfish O ile wiem, te pliki powinny być własnością użytkownika. Możesz chciećsudo chown $USER: ~/.gvfs ~/.dbus
Zanna,
1
Powinno wystarczyć zamknięcie stderr 2>&-. GNU find nie zakończy się, jeśli spróbuje zapisać komunikaty o błędach w dysfunkcyjnym deskryptorze pliku. Kwestie własności sudo chown -R $USER: ...byłyby bardziej skuteczne w przypadku większej liczby plików, które nie są własnością $USER.
David Foerster,
8

Odmowa dostępu jest prawdopodobnie drukowana, stderra nie stdout.

Spróbuj tego:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

2>&1Przekierowuje wyjście z stderrcelu stdout, dzięki czemu grep -vmoże wykonywać swoje zadania. (Domyślnie |tylko potoki, stdouta nie stderr.)

You'reAGitForNotUsingGit
źródło
ale dla tego 2> i 1 oznacza, czy istnieje stderr, wysłać na stdout?
solfish
@solfish Tak, właśnie o to chodzi :)
You'reAGitForNotUsingGit
to, czego nie rozumiem, to przed „|” jako wynik; mamy tylko stderr, prawda? i po „|” jako wkład otrzymaliśmy to
solfish
@solfish Cóż, natknąłem się na ten problem około półtora roku temu i udało mi się go rozwiązać przy użyciu innej metody . Ale potem komentarz pod moją odpowiedzią sugeruje po prostu użyć 2>&1... Nie jestem ekspertem od bash, więc jeśli to nieprawda, proszę powiedz tak :)
You'reAGitForNotUsingGit
@AndroidDev Sugeruję dodanie innej metody do tej odpowiedzi jako alternatywy. Krytyka Etana Reisnera polegała na tym , że podstawienie procesu nie jest przenośne. Ale bashw Ubuntu ma to, z wyjątkiem trybu POSIX . Myślę, że to najlepsze rozwiązanie - plik o złośliwej nazwie access deniednadal będzie się pojawiał.
Eliah Kagan,
4

Prawdopodobnie masz na myśli „Odmowa finddostę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):

(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

(Zwykle chcesz przekazać kilka argumentów find. Te idą przed pierwszym przekierowaniem 3>&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/nulllub 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ści findzgłasza „Brak takiego pliku lub katalogu”, jeśli punkt początkowy nie istnieje. Przy wielu punktach początkowych findmoże nadal zwracać przydatne wyniki i wydawać się działać. Na przykład, jeśli ai cistnieje, ale bnie istnieje , find a b c -name xwypisuje wyniki a, następnie „Brak takiego pliku lub katalogu” dla b, a następnie powoduje c.

Łączenie stdout i stderr razem w stdout i przesyłanie potokowe do greplub inne polecenie do filtrowania go - tak jak z 2>&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óre findakcje, w tym -printakcja 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 .

  • Jeśli nie jest to terminal, nazwy plików są wypisywane w stanie „tak jak są”, nawet jeśli zawierają dziwne znaki, takie jak znaki nowej linii i znaki kontrolne, które mogą zmienić zachowanie twojego terminala. Jeśli jest to terminal, znaki te są pomijane i ?zamiast tego są drukowane.
  • To jest zwykle to, czego chcesz. Jeśli zamierzasz dalej przetwarzać nazwy plików, muszą być one wypisane dosłownie. Jednak jeśli zamierzasz je wyświetlić, nazwa pliku z nowym wierszem mogłaby naśladować wiele nazw plików, a nazwa pliku z sekwencją znaków backspace może wyglądać inaczej. Możliwe są również inne problemy, takie jak nazwy plików zawierające sekwencje specjalne, które zmieniają kolory w twoim terminalu.
  • Ale potokowanie wyników wyszukiwania przez inne polecenie (jak grep) powoduje, że findnie 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óry findjest terminalem wykrywanie ma na celu zapobieganie.
  • Aby man finduzyskać 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 findimplementacji 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 tym bash) obsługują |&potokowanie obu strumieni - lub możesz najpierw przekierować stderr na stdout 2>&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 grepfiltrowania:

find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'

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 miejsce argswyż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.

  • Deskryptor pliku 1 to stdout, a 2 to stderr (a bez przekierowania 0 to stdin ). Ale możesz również przekierowywać za pomocą innych deskryptorów plików. Można to wykorzystać do otwarcia lub otwarcia pliku lub urządzenia.
  • 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>&2przekierowuje 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.
  • Aby uzyskać więcej informacji, zobacz Jak stderr rur, a nie stdout? i IO Redirection - Zamiana stdout i stderr (Advanced), a zwłaszcza tylko stderr rury przez filtr .

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:

(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

Działa to grupowanie, a nie podpowłoka. Jeśli wolisz, możesz użyć { ;}:

{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-

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ć findstderr greppolecenia na polecenie, które je filtruje, i przekierować grepstdout tego polecenia na stderr.

find args 2> >(grep -Fv 'Permission denied' >&2)

Podziękowania dla Android Dev za ten pomysł.

Chociaż bash obsługuje podstawianie procesów, shw Ubuntu jest dash, 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, gdy bashdziała w trybie POSIX , obsługa zastępowania procesów jest wyłączona.

Jedną z sytuacji, w której bashdziała w trybie POSIX, jest wywołanie go jako sh1 . Dlatego w systemie operacyjnym takim jak Fedora, gdzie bashjest to możliwe /bin/sh, lub jeśli na Ubuntu /bin/shustawiłeś symboliczne łącze do bashsiebie, podstawianie procesów nadal nie działa w shskrypcie, 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 bashautomatycznie włącza tryb POSIX po uruchomieniu poleceń w skryptach startowych.

Przykład

Warto przetestować te polecenia. Aby to zrobić, tworzę tmppodkatalog 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.

mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b

Jednym z katalogów, które jest dostępne zawiera plik „Permission denied” w nazwie. Uruchamianie findbez przekierowania lub rur pokazuje ten plik, ale również pokazuje rzeczywiste „Permission denied” błąd do innego katalogu, który nie jest dostępny:

ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied

Przesłanie zarówno stdout, jak i stderr do grepi 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:

ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b

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:

ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b

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 findz bieżącym katalogiem ( .) jako jednym punktem początkowym, ale nieistniejącym katalogiem foojako innym:

ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: foo’: No such file or directory

Sprawdzanie, czy findstandardowe wyjście jest nadal terminalem

Moż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 tmpkatalogu).

Utwórz plik z nową linią w nazwie:

touch $'abc\ndef'

Zwykle używamy katalogów jako punktów wyjścia find, ale pliki też działają:

$ find abc*
abc?def

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 abci def. Możemy to przetestować za pomocą cat:

$ find abc* | cat
abc
def

Przekierowanie tylko stderr nie powoduje tego problemu:

$ find abc* 2>/dev/null
abc?def

Ani też go nie zamyka:

$ find abc* 2>&-
abc?def

Rurociągów do grep robi przyczyną problemu:

$ find abc* |& grep -Fv 'Permission denied'
abc
def

(Zastąpienie |&przez 2>&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 :

$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def

Grupowanie tego polecenia i zamiana strumieni z powrotem nie powoduje problemu:

$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def

( { ;}Wersja daje takie same dane wyjściowe.)

Zastosowanie substytucji procesu do filtrowania stderr nie powoduje problemu:

$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def
Eliah Kagan
źródło