Ta odpowiedź składa się z następujących części:
- Podstawowe użycie
-exec
- Używanie
-execw połączeniu zsh -c
- Za pomocą
-exec ... {} +
- Za pomocą
-execdir
Podstawowe użycie -exec
-execOpcja bierze narzędzia zewnętrznego z opcjonalnymi argumentami jako argument i wykonuje go.
Jeśli ciąg {}występuje w dowolnym miejscu danego polecenia, każde jego wystąpienie zostanie zastąpione aktualnie przetwarzaną nazwą ścieżki (np ./some/path/FILENAME.). W większości powłok te znaki {}nie muszą być cytowane.
Polecenie musi zostać zakończone znakiem „ ;for”, findaby wiedzieć, gdzie się ono kończy (ponieważ mogą pojawić się dalsze opcje). Aby chronić ;powłokę przed powłoką, należy ją cytować jako \;lub ';', w przeciwnym razie powłoka zobaczy ją jako koniec findpolecenia.
Przykład ( \na końcu pierwszych dwóch linii są tylko kontynuacje linii):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
Znajduje to wszystkie zwykłe pliki ( -type f), których nazwy pasują do wzorca *.txtw bieżącym katalogu lub poniżej. Następnie przetestuje, czy ciąg hellowystępuje w dowolnym ze znalezionych plików przy użyciu grep -q(który nie generuje żadnych danych wyjściowych, tylko status wyjścia). Dla tych plików, które zawierają ciąg, catzostaną wykonane w celu wyprowadzenia zawartości pliku do terminala.
Każdy -execdziała również jak „test” na znalezionych ścieżkach find, podobnie jak -typei -namerobi. Jeśli polecenie zwraca zerowy status wyjścia (oznaczający „sukces”), findrozważana jest kolejna część polecenia, w przeciwnym razie findpolecenie będzie kontynuowane z następną nazwą ścieżki. Jest to używane w powyższym przykładzie, aby znaleźć pliki zawierające ciąg znaków hello, ale zignorować wszystkie inne pliki.
Powyższy przykład ilustruje dwa najczęstsze przypadki użycia -exec:
- Jako test do dalszego ograniczenia wyszukiwania.
- Aby wykonać jakąś akcję na znalezionej nazwie ścieżki (zwykle, ale niekoniecznie, na końcu
findpolecenia).
Używanie -execw połączeniu zsh -c
Polecenie, które -execmożna wykonać, jest ograniczone do zewnętrznego narzędzia z opcjonalnymi argumentami. Bezpośrednie używanie wbudowanych powłok, funkcji, warunków, potoków, przekierowań itp. -execNie jest możliwe, chyba że jest zapakowane w coś w rodzaju sh -cpowłoki potomnej.
Jeśli bashwymagane są funkcje, użyj bash -czamiast sh -c.
sh -cdziała /bin/shze skryptem podanym w wierszu poleceń, a następnie opcjonalnymi argumentami wiersza poleceń dla tego skryptu.
Prosty przykład użycia sh -csamego, bez find:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
To przekazuje dwa argumenty do skryptu powłoki potomnej:
Ciąg sh. Będzie to dostępne $0w skrypcie, a jeśli wewnętrzna powłoka wyświetli komunikat o błędzie, poprzedzi go tym ciągiem.
Argument applesjest dostępna $1w skrypcie, a gdyby nie było więcej argumentów, to te byłyby dostępne $2, $3itd. Będą one również dostępne na liście "$@"(z wyjątkiem $0, który nie będzie częścią "$@").
Jest to przydatne w połączeniu z, -execponieważ pozwala nam tworzyć dowolnie złożone skrypty, które działają na ścieżki znalezione przez find.
Przykład: Znajdź wszystkie zwykłe pliki, które mają określony sufiks nazwy pliku, i zmień ten sufiks nazwy pliku na inny, gdzie sufiksy 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 wewnętrznego skryptu $1będzie ciąg znaków text, $2ciąg znaków txti $3będzie to dowolna findznaleziona dla nas nazwa ścieżki . Rozszerzenie parametru ${3%.$1}wziąłoby nazwę ścieżki i usunęło .textz niej przyrostek .
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 skrypcie wewnętrznym:
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" {} ';'
Zauważ, że w tej ostatniej odmianie zmienne fromi topowłoka potomna różnią się od zmiennych o tych samych nazwach w skrypcie zewnętrznym.
Powyżej jest prawidłowy sposób wywoływania dowolnego skryptu złożonego -execz find. Używanie findw pętli jak
for pathname in $( find ... ); do
jest podatny na błędy i nieelegancki (opinia osobista). Dzieli nazwy plików na białe znaki, wywołuje globbing nazw plików, a także zmusza powłokę do rozwinięcia pełnego wyniku findprzed uruchomieniem pierwszej iteracji pętli.
Zobacz też:
Za pomocą -exec ... {} +
Na ;końcu można zastąpić +. Powoduje findto wykonanie podanej komendy z jak największą liczbą argumentów (znalezionych nazw ścieżek), a nie jeden raz dla każdej znalezionej nazwy ścieżki. Ciąg {} musi wystąpić tuż przed tym, +aby to zadziałało .
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
Tutaj findzbierze powstałe nazwy ścieżek i wykona je catna jak największej liczbie jednocześnie.
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
Podobnie w tym przypadku mvzostanie wykonany tak mało, jak to możliwe. Ten ostatni przykład wymaga GNU mvz coreutils (który obsługuje tę -topcję).
Użycie -exec sh -c ... {} +jest również skutecznym sposobem na zapętlenie zestawu ścieżek za pomocą dowolnego, złożonego skryptu.
Podstawy są takie same jak przy użyciu -exec sh -c ... {} ';', ale skrypt zajmuje teraz znacznie dłuższą listę argumentów. Można je zapętlać, zapętlając "$@"w skrypcie.
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" {} +
Za pomocą -execdir
Istnieje również -execdir(zaimplementowane przez większość findwariantów, ale nie jest to standardowa opcja).
Działa to -execz tą różnicą, że dane polecenie powłoki jest wykonywane z katalogiem znalezionej nazwy ścieżki jako bieżącym katalogiem roboczym, który {}będzie zawierał nazwę basenową znalezionej nazwy ścieżki bez jej ścieżki (ale GNU findnadal będzie poprzedzać nazwę bazy ./, podczas gdy BSD findtego nie zrobi).
Przykład:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
Spowoduje to przeniesienie każdego znalezionego *.txtpliku do wcześniej istniejącego done-textspodkatalogu w tym samym katalogu, w którym znaleziono plik . Nazwa pliku zostanie również zmieniona przez dodanie .donedo niego sufiksu .
Byłoby to nieco trudniejsze do zrobienia, -execponieważ musielibyśmy pobrać nazwę bazową znalezionego pliku, {}aby utworzyć nową nazwę pliku. Potrzebujemy również nazwy katalogu, {}aby done-textspoprawnie zlokalizować katalog.
Z -execdirniektórymi takimi rzeczami staje się łatwiejsze.
Odpowiednia operacja wykorzystująca -execzamiast -execdirmusiałaby użyć powłoki potomnej:
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 {} +