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?
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?
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 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ć.
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ści sudo 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 nie stdout
.
Spróbuj tego:
find /home -iname "*.pdf" 2>&1 | grep -v "access denied"
2>&1
Przekierowuje wyjście z stderr
celu stdout
, dzięki czemu grep -v
może wykonywać swoje zadania. (Domyślnie |
tylko potoki, stdout
a nie stderr
.)
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 nazwie access 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):
(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.
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ści find
zgłasza „Brak takiego pliku lub katalogu”, jeśli punkt początkowy nie istnieje. Przy wielu punktach początkowych find
może nadal zwracać przydatne wyniki i wydawać się działać. Na przykład, jeśli a
i c
istnieje, ale b
nie istnieje , find a b c -name x
wypisuje 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 grep
lub 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 find
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, że find
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óry find
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.
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 grep
filtrowania:
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 args
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:
(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>&-
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
stderr grep
polecenia na polecenie, które je filtruje, i przekierować grep
stdout tego polecenia na stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
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 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 bash
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 jako sh
1 . Dlatego w systemie operacyjnym takim jak Fedora, gdzie bash
jest to możliwe /bin/sh
, lub jeśli na Ubuntu /bin/sh
ustawiłeś symboliczne łącze do bash
siebie, podstawianie procesów nadal nie działa w sh
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.
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
.
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 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:
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 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:
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 find
z bieżącym katalogiem ( .
) jako jednym punktem początkowym, ale nieistniejącym katalogiem foo
jako 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
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:
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 abc
i 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