Używanie konektorów po poleceniu find

10

Chcę, aby mój bash wypisał „znaleziono” tylko wtedy, gdy coś zostanie znalezione, za pomocą polecenia find. Ale użycie && nie pomaga: nawet jeśli nic nie znalazłem, drukuje się „znaleziono”. Przykład:

$ pwd
/data/data/com.termux/files/home/test/test1/test4
$ ls
xaa  xab
$ find . -name xac && echo 'found'
found
$ find . -name xaa && echo 'found'
./xaa
found
Josef Klimuk
źródło

Odpowiedzi:

18

Możesz findsamodzielnie wydrukować found:

find . -name xac -printf "found\n" -quit

-quitPozwoli find zakończyć po pierwszym meczu , więc foundjest drukowana tylko co najwyżej raz.

W podobnym wątku w systemach Unix i Linux ( make find fail, gdy nic nie znaleziono ), grep -qzzwracałem niezerowy status wyjścia, jeśli findnic nie znalazłem:

find /some/path -print0 -quit | grep -qz .

Które można wykorzystać do budowy złożonych poleceń za pomocą &&albo if:

find /some/path -print0 -quit | grep -qz . && echo found
muru
źródło
Przez chwilę musiałem na to patrzeć. /some/pathmówi, od czego zacząć, ale nic nie mówi, czego szukać. To samo w połączonej odpowiedzi. Co dla mnie działa find /some/path -name xac -print0 -quit | grep -qz . && echo found. Przegapiłem coś?
Joe
@ Joe liczy się tutaj -print0 -quit. To, co umieścisz wcześniej, zależy od tego, co chcesz znaleźć. Zdecydowałem się to tutaj pominąć.
muru
13

Odpowiedź muru jest odpowiednia i dobrze nadaje się do przypadków, w których chcemy coś wydrukować, jeśli plik zostanie znaleziony. W ogólnym przypadku, gdy chcemy wykonać polecenie zewnętrzne, np. echoMożemy użyć -execflagi.

$ find . -name 'xac' -exec echo "I found " {} \; -quit             
I found  ./xac

{}Część przechodzi pliku do polecenia pomiędzy -execi \;jako argumenty. Zauważ, że \przed ;- zapobiega powłoki z misinterpreting go; w muszli zamykania oznacza rednikami końca polecenia, ale gdy uciekł z kreską, powłoka będzie traktować go jako tekst literall być przekazane do findpolecenia i znaleźć polecenie służy jako zamknięcie -execargumenty bandery.


Do konstruowania tego if found do this; else do thatrodzaju warunków możemy użyć podstacji polecenia $()i testpolecenia (aka [):

$ [ "x$(find . -name 'noexist' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                              
not found

$ [ "x$(find . -name 'xac' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                                  
found

Zwracając się do komentarza Dana

Dan w komentarzach zapytał:

Czy echo „Znalazłem {}” nie byłoby lepsze niż echo „Znalazłem" {}? Może dla echa jest w porządku, ale jeśli ktoś skopiuje polecenie i zastąpi echo innym poleceniem, może mieć problem

Najpierw zrozummy problem. Zwykle w powłokach występuje koncepcja podziału słów, co oznacza, że ​​niecytowane zmienne i parametry pozycyjne będą rozszerzane i traktowane jako osobne elementy. Na przykład, jeśli masz zmienną vari zawiera hello worldtekst, gdy zrobisz touch $varpowłoka będzie rozbicie go na dwie odrębne pozycje helloi worldi touchzrozumie, że tak, jakbyś próbował utworzyć 2 oddzielne pliki; jeśli to zrobisz touch "$var", powłoka będzie traktowana hello worldjako jedna jednostka i touchutworzy tylko jeden plik. Ważne jest, aby zrozumieć, że dzieje się tak tylko z powodu działania powłok.

W przeciwieństwie do tego, findnie cierpi z powodu takiego zachowania, ponieważ polecenia są przetwarzane przez findsiebie i wykonywane przez execvp()wywołanie systemowe, więc nie ma w tym żadnej powłoki. Podczas gdy nawiasy klamrowe mają specjalne znaczenie w powłokach, ponieważ pojawiają się w środku findpolecenia, a nie na początku, w tym przypadku nie mają specjalnego znaczenia dla powłoki. Oto przykład. Utwórzmy kilka trudnych nazw plików i spróbuj przekazać je jako argument do statpolecenia.

$ touch with$'\t'tab.txt with$' 'space.txt with$'\n'newline.txt

$ find -type f -exec stat -c "%F" {} \; -print                                                                                                                         
regular empty file
./with?newline.txt
regular empty file
./with space.txt
regular empty file
./with?tab.txt

Jak widać, statdoskonale odbiera trudne nazwy plików find, co jest jednym z głównych powodów, dla których jest zalecane do użycia w przenośnych skryptach, a szczególnie przydatne, gdy przeglądasz drzewo katalogów i chcesz zrobić coś z nazwami plików, które mogą mieć znaki specjalne w nich. Dlatego nie jest konieczne cytowanie nawiasów klamrowych dla poleceń wykonywanych w find.

To inna historia, kiedy angażuje się Shell. Czasami musisz użyć powłoki do przetworzenia nazwy pliku. W takim przypadku cytowanie będzie miało znaczenie, ale ważne jest, aby zdawać sobie sprawę, że nie ma problemu ze znalezieniem - to powłoka dzieli słowa.

$ find -type f -exec bash -c "stat {}" sh \;   
stat: cannot stat './with': No such file or directory
sh: line 1: newline.txt: command not found
stat: cannot stat './with': No such file or directory
stat: cannot stat 'space.txt': No such file or directory
stat: cannot stat './with': No such file or directory
stat: cannot stat 'tab.txt': No such file or directory

Więc kiedy cytujemy w powłoce , to zadziała. Ale znowu, to ważne dla powłoki, nie find.

$ find -type f -exec bash -c "stat -c '%F' '{}'" sh \;                                                                                                                 
regular empty file
regular empty file
regular empty file
Sergiy Kolodyazhnyy
źródło
Nie echo "I found {}"byłoby lepiej niż echo "I found " {}? Może dla echa jest w porządku, ale jeśli ktoś skopiuje polecenie i zastąpi echo innym poleceniem, może mieć problem.
Dan
@ Czy temat był zbyt długi, aby omawiać go w komentarzach, więc zmieniłem odpowiedź. Proszę zobaczyć
Sergiy Kolodyazhnyy
1
Dzięki, że w końcu zrozumiałem, dlaczego ten średnik musiał tam być. Świetne wyjaśnienie cytowania.
Joe
1
Nie spodziewałam się tak wielu szczegółów jako odpowiedzi na mój komentarz. Bardzo doceniam to wyjaśnienie, dzięki!
Dan