Ciągle sprawdzam składnię

find . -name "FILENAME" -exec rm {} \; 

głównie dlatego, że nie rozumiem, jak dokładnie działa część -exec. Jakie jest znaczenie nawiasów klamrowych, ukośnika odwrotnego i średnika? Czy są inne przypadki użycia tej składni?

Komentarze

  • @Philippos: Rozumiem. Proszę pamiętać, że strony podręcznika są odniesieniami, tj. przydatne dla tych ze zrozumieniem sprawy, aby sprawdzić składnię. Dla kogoś nowego w temacie często są zbyt tajemnicze i formalne, aby były użyteczne. Przekonasz się, że zaakceptowana odpowiedź jest około 10 razy dłuższa niż wpis na stronie podręcznika, oraz że ' nie bez powodu.
  • Nawet stara strona POSIX man czyta nazwa_narzędzia lub argument zawierające tylko dwa znaki " {} " należy zastąpić d przez obecną ścieżkę , która wydaje mi się wystarczająca. Dodatkowo ma przykład z -exec rm {} \;, tak jak w twoim pytaniu. Za moich czasów nie było prawie żadnych innych zasobów niż " duża szara ściana ", drukowane książki man stron (papier był tańszy niż przechowywanie). Więc wiem, że to wystarczy dla kogoś nowego w temacie. Możesz zadać ostatnie pytanie tutaj. Niestety ani @Kusalananda, ani ja nie mamy na to odpowiedzi.
  • @Philippos 🙂 Och, to byłoby ekscytującą rzeczą do wypróbowania na poziomie " ekscytujący ", który wykracza poza skalę mojego wykresu.
  • Comeon @Philippos. Czy naprawdę mówisz Kusalanandzie, że nie poprawił strony podręcznika? 🙂
  • @ZsoltSzilagy Nie powiedziałem tego ani nie miałem tego na myśli. Bardzo dobrze cię karmił, po prostu myślę, że jesteś na tyle dorosły, żeby jeść samemu. (-;

Odpowiedź

Ta odpowiedź jest podzielona na następujące części:

  • Podstawowe użycie -exec
  • Używanie -exec w połączeniu z sh -c
  • Używanie -exec ... {} +
  • Używanie -execdir

Podstawowe użycie -exec

Opcja -exec przyjmuje zewnętrzne narzędzie z opcjonalnymi argumentami jako jego argument i wykonuje go.

Jeśli ciąg {} znajduje się w dowolnym miejscu w danym poleceniu, każde jego wystąpienie zostanie zastąpione aktualnie przetwarzaną nazwą ścieżki ( np. ./some/path/FILENAME). W większości powłok dwa znaki {} nie muszą być cytowane.

Polecenie musi być zakończone ; dla find, aby wiedzieć, gdzie się kończy (ponieważ później mogą być dalsze opcje s). Aby chronić ; przed powłoką, należy go cytować jako \; lub ";" , w przeciwnym razie powłoka zobaczy to jako koniec polecenia find.

Przykład (\ w koniec pierwszych dwóch wierszy służy tylko do kontynuacji wiersza):

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";" 

Spowoduje to znalezienie wszystkich zwykłych plików (-type f) których nazwy pasują do wzorca *.txt w bieżącym katalogu lub poniżej. Następnie sprawdzi, czy ciąg hello występuje w którymkolwiek ze znalezionych plików przy użyciu grep -q (co nie daje żadnego wyniku, tylko wyjście status). W przypadku plików zawierających ciąg znaków cat zostanie wykonany w celu wyświetlenia zawartości pliku na terminalu.

Każdy -exec działa również jak „test” na ścieżkach znalezionych przez find, podobnie jak -type i -name tak. Jeśli polecenie zwróci zerowy kod zakończenia (oznaczający „powodzenie”), rozważana jest następna część polecenia find, w przeciwnym razie find Polecenie jest kontynuowane z następną nazwą ścieżki. Jest to używane w powyższym przykładzie do znalezienia plików zawierających ciąg hello, ale do zignorowania wszystkich innych plików.

Powyższy przykład ilustruje dwa najpopularniejsze zastosowania przypadki -exec:

  1. Jako test w celu dalszego ograniczenia wyszukiwania.
  2. Aby wykonać jakąś akcję na znalezionym nazwa ścieżki (zwykle, ale niekoniecznie, na końcu polecenia find).

Używanie -exec w połączeniu z sh -c

Polecenie, które -exec może wykonać, jest ograniczone do zewnętrznego narzędzia z opcjonalnymi argumentami.Używanie wbudowanych elementów powłoki, funkcji, warunków, potoków, przekierowań itp. Bezpośrednio z -exec nie jest możliwe, chyba że jest opakowane w coś takiego jak sh -c child shell.

Jeśli bash funkcje są wymagane, użyj bash -c zamiast sh -c.

sh -c uruchamia /bin/sh ze skryptem podanym w wierszu poleceń, po którym następują opcjonalne argumenty wiersza poleceń do tego skryptu.

Prosty przykład użycia samego sh -c, bez find :

sh -c "echo "You gave me $1, thanks!"" sh "apples" 

Przekazuje dwa argumenty do potomnego skryptu powłoki. Zostaną one umieszczone w $0 i $1, aby skrypt mógł użyć.

  1. string sh. Będzie on dostępny jako $0 wewnątrz skryptu, a jeśli wewnętrzna powłoka wyświetli komunikat o błędzie, będzie poprzedzać go tym ciągiem.

  2. Argument apples jest dostępny jako $1 w skrypcie i gdyby było więcej argumentów, byłyby one dostępne jako $2, $3 itd. Będą również dostępne na liście "$@" (z wyjątkiem $0, który nie byłby częścią "$@").

Jest to przydatne w połączeniu z -exec, ponieważ pozwala nam tworzyć dowolnie złożone skrypty działające na ścieżkach znalezionych przez find.

Przykład: Znajdź wszystkie zwykłe pliki, które mają określony przyrostek nazwy pliku i zmień ten przyrostek na inny, gdzie przyrostki są przechowywane w zmiennych:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c "mv "$3" "${3%.$1}.$2"" sh "$from" "$to" {} ";" 

Wewnątrz inte rnal script, $1 będzie ciągiem text, $2 będzie ciągiem txt i $3 byłyby dowolną ścieżką znalezioną przez find. Rozwinięcie parametru ${3%.$1} pobrałoby nazwę ścieżki i usunąłoby z niej przyrostek .text.

Lub używając dirname / basename:

find . -type f -name "*.$from" -exec sh -c " mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"" sh "$from" "$to" {} ";" 

lub z dodanymi zmiennymi w skrypt wewnętrzny:

find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2; pathname=$3 mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"" sh "$from" "$to" {} ";" 

Zwróć uwagę, że w tej ostatniej odmianie zmienne from i to w powłoce potomnej różnią się od zmiennych o takich samych nazwach w zewnętrznym skrypcie.

Powyższe jest poprawnym sposobem wywołania dowolnego złożonego skryptu z -exec z find. Używanie find w pętli takiej jak

for pathname in $( find ... ); do 

jest podatne na błędy i nieeleganckie (osobiste zdanie). Dzieli nazwy plików na białe znaki, wywołuje globalizację nazw plików, a także zmusza powłokę do rozwinięcia pełnego wyniku find przed uruchomieniem pierwszej iteracji pętli.

Zobacz też:


Używając -exec ... {} +

; na końcu można zastąpić +. Powoduje to, że find wykonuje dane polecenie z jak największą liczbą argumentów (znalezionych nazw ścieżek), a nie raz dla każdej znalezionej ścieżki. Aby to zadziałało, ciąg {} musi wystąpić tuż przed +.

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} + 

Tutaj find zbierze otrzymane nazwy ścieżek i wykona cat na jak największej liczbie z nich naraz.

find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec mv -t /tmp/files_with_hello/ {} + 

Podobnie, mv zostanie wykonane jako kilka razy, jak to możliwe. Ten ostatni przykład wymaga GNU mv z coreutils (który obsługuje opcję -t).

Używanie -exec sh -c ... {} + to także skuteczny sposób na zapętlenie zestawu nazw ścieżek z dowolnie złożonym skryptem.

Podstawy są takie same, jak w przypadku używania -exec sh -c ... {} ";", ale skrypt przyjmuje teraz znacznie dłuższą listę argumentów. Można je zapętlić, wykonując pętlę nad "$@" wewnątrz skryptu.

Nasz przykład z ostatniej sekcji, która zmienia sufiksy nazw plików:

from=text # Find files that have names like something.text to=txt # Change the .text suffix to .txt find . -type f -name "*.$from" -exec sh -c " from=$1; to=$2 shift 2 # remove the first two arguments from the list # because in this case these are *not* pathnames # given to us by find for pathname do # or: for pathname in "$@"; do mv "$pathname" "${pathname%.$from}.$to" done" sh "$from" "$to" {} + 

Używanie -execdir

Istnieje również -execdir ( implementowane przez większość wariantów find, ale nie jest to opcja standardowa).

Działa to jak -exec z tą różnicą, że dane polecenie powłoki jest wykonywane z katalogiem o znalezionej ścieżce jako bieżącym katalogiem roboczym i że będzie zawierało nazwę podstawową znalezionej ścieżki bez ścieżki (ale GNU find będzie nadal poprzedzać nazwę basenową znakiem ./, podczas gdy BSD find tego nie zrobi).

Przykład:

find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \; 

Spowoduje to przeniesienie każdego znalezionego *.txt -pliku do wcześniej istniejącego done-texts podkatalogu w tym samym katalogu, w którym plik był znaleziono . Nazwa pliku zostanie również zmieniona przez dodanie do niego przyrostka .done.

Byłoby to nieco trudniejsze w przypadku -exec, ponieważ musielibyśmy pobrać nazwę podstawową znalezionego pliku z {}, aby utworzyć nową nazwę pliku. Potrzebujemy również nazwy katalogu z {}, aby poprawnie zlokalizować katalog done-texts.

Z -execdir, niektóre takie rzeczy stają się łatwiejsze.

Odpowiednia operacja przy użyciu -exec zamiast -execdir musiałby zastosować powłokę potomną:

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done" done" sh {} + 

lub

find . -type f -name "*.txt" -exec sh -c " for name do mv "$name" "${name%/*}/done-texts/${name##*/}.done" done" sh {} + 

Komentarze

  • -exec pobiera program i argumenty i uruchamia go; niektóre polecenia powłoki składają się tylko z programu i argumentów, ale wiele z nich nie. Polecenie powłoki może obejmować przekierowanie i orurowanie; -exec nie może (chociaż całe find można przekierować). Polecenie powłoki może używać ; && if itp.; -exec nie może, chociaż -a -o może coś zrobić. Polecenie powłoki może być aliasem, funkcją powłoki lub funkcją wbudowaną; -exec nie może. Polecenie powłoki może rozwinąć vars; -exec nie może (chociaż zewnętrzna powłoka obsługująca find może). Polecenie powłoki może za każdym razem zastąpić $(command) inaczej; -exec nie może. …
  • Mówiąc to ' polecenie powłoki jest tutaj nieprawidłowe, find -exec cmd arg \; nie ' t wywołuje powłokę w celu zinterpretowania wiersza poleceń powłoki, uruchamia ona execlp("cmd", "arg") bezpośrednio, a nie execlp("sh", "-c", "cmd arg") (dla którego Powłoka zrobiłaby odpowiednik execlp("cmd", "arg"), gdyby cmd nie zostało wbudowane).
  • Możesz wyjaśnić, że wszystko argumenty find po -exec i do ; lub + tworzy polecenie do wykonania wraz z jego argumentami, z każdym wystąpieniem argumentu {} zastępowanym bieżącym plikiem (z ;) i {} jako ostatni argument przed + zastąpionymi listą plików jako oddzielnymi argumentami (w {} + case). IOW -exec przyjmuje kilka argumentów, zakończonych ; lub {} +.
  • @Kusalananda Wouldn ' t Twój ostatni przykład działa również z tym prostszym poleceniem: find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'?
  • @Atralb Tak, to również zadziałałoby i miałby taki sam efekt jak ostatni fragment kodu, ale zamiast uruchamiać mv w pętli, raz na znaleziony plik, wykonujesz zarówno sh, jak i mv dla każdego znalezionego pliku, co będzie zauważalnie wolniej w przypadku dużych ilości plików.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *