Używanie średnika (;) vs plus (+) z exec w funkcji find

159

Dlaczego istnieje różnica w wynikach między użyciem

find . -exec ls '{}' \+

i

find . -exec ls '{}' \;

Mam:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1
Ankur Agarwal
źródło

Odpowiedzi:

249

Najlepiej można to zilustrować na przykładzie. Powiedzmy, że findpowoduje wyświetlenie tych plików:

file1
file2
file3

Użycie -execze średnikiem ( find . -exec ls '{}' \;) spowoduje wykonanie

ls file1
ls file2
ls file3

Ale jeśli zamiast tego użyjesz znaku plus ( find . -exec ls '{}' \+), jak najwięcej nazw plików zostanie przekazanych jako argumenty do pojedynczego polecenia:

ls file1 file2 file3

Liczba nazw plików jest ograniczona jedynie maksymalną długością wiersza poleceń systemu. Jeśli polecenie przekroczy tę długość, zostanie wywołane wiele razy.

Jaskółka oknówka
źródło
1
dzięki. jest to bardzo przydatne do sortowania plików wynikowych: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas
1
Głupi q: Zauważyłem, że +skojarzenie z -execjest zawsze uciekane, ale +skojarzone z -mtimenie jest. Czy znasz powód? Myślę, że to nawyk ucieczki ;związany z -exec.
kevinarpe
3
@kevinarpe rzeczywiście, przypisałbym to przyzwyczajeniu z ;. Nie wyobrażam sobie, żeby kiedykolwiek trzeba było uciec+
Martin
36

Wszystkie dotychczasowe odpowiedzi są poprawne. Proponuję to jako jaśniejszą (dla mnie) demonstrację zachowania, które jest opisane przy użyciu, echoa nie ls:

Ze średnikiem polecenie echojest wywoływane raz na każdy znaleziony plik (lub inny obiekt systemu plików):

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

Z plusem polecenie echojest wywoływane tylko raz. Każdy znaleziony plik jest przekazywany jako argument.

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

Jeśli pojawi findsię duża liczba wyników, może się okazać, że polecenie, które nazywamy, dławi liczbę argumentów.

Johnsyweb
źródło
1
Czy funkcja find nie powinna dodawać wyników tylko do liczby, która umożliwia bezpieczne przekazanie jej do powłoki? A przynajmniej xargstak ... w zasadzie nigdy nie dusi się zbyt wieloma argumentami.
Rmano
1
@Rmano: Widziałem find(i xargs) w Solarisie emitowało więcej argumentów, niż można było zużyć. xargs(I find) w findutils` GNU wydaje się zachowywać bardziej sensownie, ale nie każdy używa GNU.
Johnsyweb,
2
@Johnsyweb, wszystkie POSIX starałyby findsię uniknąć przekroczenia limitu liczby argumentów. I to obejmuje Solaris (przynajmniej 10). Może się to nie powieść, jeśli zrobisz coś takiego , jak find ... -exec ksh -c 'cmd "$@" "$@"' sh {} +lub find ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +, ale findtak naprawdę nie można za to winić. Zwróć uwagę, że GNU findbyło jedną z ostatnich findimplementacji do obsługi +(kiedyś było to trudne do przeniesienia skryptu na systemy GNU).
Stephane Chazelas
19

Od man find:

-exec polecenie;

Wykonaj polecenie; prawda, jeśli zwracany jest status 0. Wszystkie następujące argumenty do znalezienia są traktowane jako argumenty polecenia, aż do argumentu składającego się z „;” napotkano. Ciąg „{}” jest zastępowany przez aktualnie przetwarzaną nazwę pliku wszędzie tam, gdzie występuje w argumentach polecenia, a nie tylko w argumentach, w których występuje samodzielnie, jak w niektórych wersjach find. Obie te konstrukcje mogą wymagać zmiany znaczenia (z '\') lub zacytowania, aby chronić je przed ekspansją przez powłokę. Zobacz sekcję PRZYKŁADY sec, aby zobaczyć przykłady użycia opcji '-exec'. Określone polecenie jest uruchamiane raz dla każdego dopasowanego pliku. Polecenie jest wykonywane w katalogu startowym. Istnieją nieuniknione problemy związane z bezpieczeństwem związane z używaniem opcji -exec;

-exec polecenie {} +

Ten wariant opcji -exec uruchamia określone polecenie na wybranych plikach, ale wiersz poleceń jest tworzony przez dołączenie nazwy każdego wybranego pliku na końcu ; całkowita liczba wywołań polecenia będzie znacznie mniejsza niż liczba dopasowanych plików. Linia poleceń jest zbudowana w podobny sposób, w jaki xargs buduje swoje linie poleceń. W poleceniu dozwolone jest tylko jedno wystąpienie „{}”. Polecenie jest wykonywane w katalogu startowym.

Tak więc, tak jak rozumiem, \;wykonuje osobne polecenie dla każdego znalezionego pliku find, podczas gdy \+dołącza pliki i wykonuje pojedyncze polecenie na wszystkich z nich. \Jest znakiem ucieczki, więc jest to:

ls testdir1; ls testdir2 

vs

ls testdir1 testdir2

Wykonanie powyższego w mojej powłoce odzwierciedlało wynik twojego pytania.

przykład, kiedy chciałbyś użyć \+

Załóżmy, że dwa pliki 1.tmpi 2.tmp:

1. tmp:

1
2
3

2. tmp:

0
2
3

Z \;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

Natomiast jeśli używasz \+(do łączenia wyników find):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

W tym przypadku jest to różnica między diff 1.tmp; diff 2.tmpidiff 1.tmp 2.tmp

Są przypadki, w których \;jest to właściwe i \+będzie konieczne. Korzystanie \+z programu with rmjest jednym z takich przypadków, w którym w przypadku usuwania dużej liczby plików wydajność (szybkość) będzie wyższa \;.

matchew
źródło
Mogę też przeczytać stronę podręcznika. I tak, ale nie sądzę, że rozumiem różnicę między używaniem; vs +
Ankur Agarwal
Nie sądzę, żeby -1 był sprawiedliwy, wyjaśniłem swoje rozumienie tego człowieka. Nie tylko skopiowałem mężczyznę i nie wyszedłem. ale zredagowałem moją odpowiedź, aby zawierała lepszy przykład.
matchew
10

findma specjalną składnię. Używasz takich, {}jakie są, ponieważ mają one znaczenie do znalezienia jako ścieżki do znalezionego pliku, a (większość) powłok nie interpretuje ich inaczej. Potrzebujesz odwrotnego ukośnika, \;ponieważ średnik ma znaczenie dla powłoki, która zjada go, zanim będzie findmógł go uzyskać. Więc to, co findchce zobaczyć PO zakończeniu powłoki, na liście argumentów przekazanej do programu w C, to

„-exec”, „rm”, „{}”, „;”

ale potrzebujesz \;w wierszu poleceń, aby uzyskać średnik przez powłokę do argumentów.

Możesz uciec, \{\}ponieważ cytowana przez powłokę interpretacja \{\}jest słuszna {}. Podobnie możesz użyć „{}”.

To, czego nie możesz zrobić, to użyć

 -exec 'rm {} ;'

ponieważ powłoka interpretuje to jako jeden argument,

"-exec", "rm {};"

i rm {} ;nie jest nazwą polecenia. (Przynajmniej chyba, że ​​ktoś naprawdę się kręci.)

Aktualizacja

różnica jest pomiędzy

$ ls file1
$ ls file2

i

$ ls file1 file2

+Jest catenating nazwy na linii poleceń.

Charlie Martin
źródło
1
Rozumiem, co mówisz. Pytam jaka jest różnica między używaniem; vs +
Ankur Agarwal
1
przepraszam, ale czy uważnie przeczytałeś moje pytanie lub komentarz? Może będę musiał to przeformułować. Dlaczego jest inne o / p, gdy używam średnika z exec w funkcji find, a kiedy używam plusa z exec w find?
Ankur Agarwal
2
To doskonałe wyjaśnienie DLACZEGO polecenie jest takie, że zaakceptowana odpowiedź nie obejmuje. Dzięki!
Sherwin Yu
1

Różnica między ;(średnikiem) a +(znakiem plusa) polega na tym, jak argumenty są przekazywane do parametru find -exec/ -execdir. Na przykład:

  • użycie ;spowoduje wykonanie wielu poleceń (osobno dla każdego argumentu),

    Przykład:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    Wszystkie następujące argumenty do findsą traktowane jako argumenty polecenia.

    Ciąg {}jest zastępowany przez aktualnie przetwarzaną nazwę pliku.

  • użycie +spowoduje wykonanie najmniejszej możliwej liczby poleceń (ponieważ argumenty są łączone). Jest to bardzo podobne do działania xargspolecenia, więc użyje jak największej liczby argumentów na polecenie, aby uniknąć przekroczenia maksymalnego limitu argumentów w wierszu.

    Przykład:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    Wiersz poleceń jest tworzony przez dołączenie na końcu każdej wybranej nazwy pliku.

    {}W poleceniu dozwolone jest tylko jedno wystąpienie .

Zobacz też:

kenorb
źródło
-5

próbowaliśmy znaleźć plik do sprzątania.

odnaleźć . -exec echo {} \; komenda przebiegła przez noc w końcu bez rezultatu.

odnaleźć . -exec echo {} \ + daje wyniki i zajęło tylko kilka godzin.

Mam nadzieję że to pomoże.

Ron
źródło
2
Ta odpowiedź nie wyjaśnia, jak działają te dwa sposoby i jak różnią się uzyskane przez nie wyniki.
misko321