głównie dlatego, że nie widzę, jak dokładnie -execdziała ta część. Co oznaczają nawiasy klamrowe, ukośnik odwrotny i średnik? Czy istnieją inne przypadki użycia tej składni?
@Philippos: Rozumiem twój punkt widzenia. Należy pamiętać, że strony podręcznika są odniesieniem, tj. Przydatne dla osób, które rozumieją sprawę, aby wyszukać składnię. Dla kogoś nowego w tym temacie są często tajemnicze i formalne, aby były przydatne. Przekonasz się, że zaakceptowana odpowiedź jest około 10 razy dłuższa niż wpis strony podręcznika, i to z jakiegoś powodu.
Zsolt Szilagy
6
Nawet stara manstrona POSIX brzmi : nazwa_użyteczności lub argument zawierający tylko dwa znaki „{}” należy zastąpić bieżącą nazwą ścieżki , co wydaje mi się wystarczające. Dodatkowo ma przykład -exec rm {} \;, tak jak w twoim pytaniu. W moich czasach nie było prawie żadnych innych zasobów niż „duża szara ściana”, książki z wydrukowanymi manstronami (papier był tańszy niż przechowywanie). Wiem więc, że to wystarczy dla kogoś nowego w temacie. Ostatnie pytanie można jednak zadać tutaj. Niestety, ani @Kusalananda, ani ja nie mamy na to odpowiedzi.
Philippos
1
Comeon @Philippos. Czy naprawdę mówisz Kusalanandzie, że nie poprawił strony? :-)
Zsolt Szilagy
1
@ allo Chociaż xargsczasem jest przydatny, findmoże przekazać wiele argumentów ścieżki do polecenia bez niego. -exec command... {} +( +zamiast zamiast \;) przechodzi tyle ścieżek, command...ile będzie pasować (każdy system operacyjny ma swój własny limit długości wiersza poleceń). I jak xargsThe +-zakończony forma find„s -execdziałania będzie również uruchomić command...kilka razy w rzadkich przypadkach, że istnieje zbyt wiele ścieżek, aby mieścić się w limicie.
Eliah Kagan,
2
@ZsoltSzilagy Nie powiedziałem tego ani nie miałem na myśli tego. Karmił cię bardzo dobrze, myślę, że jesteś wystarczająco dorosły, aby jeść samodzielnie. (-;
Philippos
Odpowiedzi:
90
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):
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.
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 .
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.
-execpobiera program i argumenty i uruchamia go; niektóre polecenia powłoki składają się tylko z programu i argumentów, ale wiele nie. Polecenie powłoki może obejmować przekierowanie i potokowanie; -execnie może (chociaż findmożna przekierować całość ). Polecenie powłoki może używać ; && ifetc; -execnie może, chociaż -a -omoże coś zrobić. Polecenie powłoki może być aliasem lub funkcją powłoki albo być wbudowane; -execNie mogę. Polecenie powłoki może rozszerzać zmienne; -execnie może (chociaż zewnętrzna powłoka, która uruchamia findpuszkę). Polecenie powłoki może za $(command)każdym razem zmieniać się inaczej; -execNie mogę. ...
dave_thompson_085
... Polecenie powłoki może globować, -execnie może - chociaż findmoże iterować pliki tak samo, jak większość globów, więc jest to rzadko potrzebne.
dave_thompson_085
@ dave_thompson_085 Oczywiście poleceniem powłoki może być shsama, co jest w pełni zdolne do robienia tych wszystkich rzeczy
Tavian Barnes
2
Mówiąc, że to polecenie powłoki jest tutaj niepoprawne, find -exec cmd arg \;nie wywołuje powłoki, aby zinterpretować wiersz polecenia powłoki, działa execlp("cmd", "arg")bezpośrednio, a nie execlp("sh", "-c", "cmd arg")(dla którego powłoka wykonałaby odpowiednik, execlp("cmd", "arg")gdyby cmdnie została wbudowana).
Stéphane Chazelas,
2
Można wyjaśnić, że wszystkie findargumenty po -execi do polecenia ;lub +tworzą polecenie do wykonania wraz z jego argumentami, z każdą instancją {}argumentu zamienioną na bieżący plik (z ;) i {}jako ostatni argument przed +zastąpiony listą plików jako osobne argumenty (w {} +przypadku). IOW -execprzyjmuje kilka argumentów, zakończonych znakiem ;lub {}+.
man
strona POSIX brzmi : nazwa_użyteczności lub argument zawierający tylko dwa znaki „{}” należy zastąpić bieżącą nazwą ścieżki , co wydaje mi się wystarczające. Dodatkowo ma przykład-exec rm {} \;
, tak jak w twoim pytaniu. W moich czasach nie było prawie żadnych innych zasobów niż „duża szara ściana”, książki z wydrukowanymiman
stronami (papier był tańszy niż przechowywanie). Wiem więc, że to wystarczy dla kogoś nowego w temacie. Ostatnie pytanie można jednak zadać tutaj. Niestety, ani @Kusalananda, ani ja nie mamy na to odpowiedzi.xargs
czasem jest przydatny,find
może przekazać wiele argumentów ścieżki do polecenia bez niego.-exec command... {} +
(+
zamiast zamiast\;
) przechodzi tyle ścieżek,command...
ile będzie pasować (każdy system operacyjny ma swój własny limit długości wiersza poleceń). I jakxargs
The+
-zakończony formafind
„s-exec
działania będzie również uruchomićcommand...
kilka razy w rzadkich przypadkach, że istnieje zbyt wiele ścieżek, aby mieścić się w limicie.Odpowiedzi:
Ta odpowiedź składa się z następujących części:
-exec
-exec
w połączeniu zsh -c
-exec ... {} +
-execdir
Podstawowe użycie
-exec
-exec
Opcja 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”,find
aby 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 koniecfind
polecenia.Przykład (
\
na końcu pierwszych dwóch linii są tylko kontynuacje linii):Znajduje to wszystkie zwykłe pliki (
-type f
), których nazwy pasują do wzorca*.txt
w bieżącym katalogu lub poniżej. Następnie przetestuje, czy ciąghello
występuje w dowolnym ze znalezionych plików przy użyciugrep -q
(który nie generuje żadnych danych wyjściowych, tylko status wyjścia). Dla tych plików, które zawierają ciąg,cat
zostaną wykonane w celu wyprowadzenia zawartości pliku do terminala.Każdy
-exec
działa również jak „test” na znalezionych ścieżkachfind
, podobnie jak-type
i-name
robi. Jeśli polecenie zwraca zerowy status wyjścia (oznaczający „sukces”),find
rozważana jest kolejna część polecenia, w przeciwnym raziefind
polecenie 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ówhello
, ale zignorować wszystkie inne pliki.Powyższy przykład ilustruje dwa najczęstsze przypadki użycia
-exec
:find
polecenia).Używanie
-exec
w połączeniu zsh -c
Polecenie, które
-exec
moż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.-exec
Nie jest możliwe, chyba że jest zapakowane w coś w rodzajush -c
powłoki potomnej.Jeśli
bash
wymagane są funkcje, użyjbash -c
zamiastsh -c
.sh -c
działa/bin/sh
ze skryptem podanym w wierszu poleceń, a następnie opcjonalnymi argumentami wiersza poleceń dla tego skryptu.Prosty przykład użycia
sh -c
samego, bezfind
:To przekazuje dwa argumenty do skryptu powłoki potomnej:
Ciąg
sh
. Będzie to dostępne$0
w skrypcie, a jeśli wewnętrzna powłoka wyświetli komunikat o błędzie, poprzedzi go tym ciągiem.Argument
apples
jest dostępna$1
w skrypcie, a gdyby nie było więcej argumentów, to te byłyby dostępne$2
,$3
itd. 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,
-exec
ponieważ pozwala nam tworzyć dowolnie złożone skrypty, które działają na ścieżki znalezione przezfind
.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:
Wewnątrz wewnętrznego skryptu
$1
będzie ciąg znakówtext
,$2
ciąg znakówtxt
i$3
będzie to dowolnafind
znaleziona dla nas nazwa ścieżki . Rozszerzenie parametru${3%.$1}
wziąłoby nazwę ścieżki i usunęło.text
z niej przyrostek .Lub używając
dirname
/basename
:lub z dodanymi zmiennymi w skrypcie wewnętrznym:
Zauważ, że w tej ostatniej odmianie zmienne
from
ito
powł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
-exec
zfind
. Używaniefind
w pętli jakjest 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
find
przed uruchomieniem pierwszej iteracji pętli.Zobacz też:
Za pomocą
-exec ... {} +
Na
;
końcu można zastąpić+
. Powodujefind
to 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 .Tutaj
find
zbierze powstałe nazwy ścieżek i wykona jecat
na jak największej liczbie jednocześnie.Podobnie w tym przypadku
mv
zostanie wykonany tak mało, jak to możliwe. Ten ostatni przykład wymaga GNUmv
z coreutils (który obsługuje tę-t
opcję).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:
Za pomocą
-execdir
Istnieje również
-execdir
(zaimplementowane przez większośćfind
wariantów, ale nie jest to standardowa opcja).Działa to
-exec
z 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 GNUfind
nadal będzie poprzedzać nazwę bazy./
, podczas gdy BSDfind
tego nie zrobi).Przykład:
Spowoduje to przeniesienie każdego znalezionego
*.txt
pliku do wcześniej istniejącegodone-texts
podkatalogu w tym samym katalogu, w którym znaleziono plik . Nazwa pliku zostanie również zmieniona przez dodanie.done
do niego sufiksu .Byłoby to nieco trudniejsze do zrobienia,
-exec
ponieważ musielibyśmy pobrać nazwę bazową znalezionego pliku,{}
aby utworzyć nową nazwę pliku. Potrzebujemy również nazwy katalogu,{}
abydone-texts
poprawnie zlokalizować katalog.Z
-execdir
niektórymi takimi rzeczami staje się łatwiejsze.Odpowiednia operacja wykorzystująca
-exec
zamiast-execdir
musiałaby użyć powłoki potomnej:lub,
źródło
-exec
pobiera program i argumenty i uruchamia go; niektóre polecenia powłoki składają się tylko z programu i argumentów, ale wiele nie. Polecenie powłoki może obejmować przekierowanie i potokowanie;-exec
nie może (chociażfind
można przekierować całość ). Polecenie powłoki może używać; && if
etc;-exec
nie może, chociaż-a -o
może coś zrobić. Polecenie powłoki może być aliasem lub funkcją powłoki albo być wbudowane;-exec
Nie mogę. Polecenie powłoki może rozszerzać zmienne;-exec
nie może (chociaż zewnętrzna powłoka, która uruchamiafind
puszkę). Polecenie powłoki może za$(command)
każdym razem zmieniać się inaczej;-exec
Nie mogę. ...-exec
nie może - chociażfind
może iterować pliki tak samo, jak większość globów, więc jest to rzadko potrzebne.sh
sama, co jest w pełni zdolne do robienia tych wszystkich rzeczyfind -exec cmd arg \;
nie wywołuje powłoki, aby zinterpretować wiersz polecenia powłoki, działaexeclp("cmd", "arg")
bezpośrednio, a nieexeclp("sh", "-c", "cmd arg")
(dla którego powłoka wykonałaby odpowiednik,execlp("cmd", "arg")
gdybycmd
nie została wbudowana).find
argumenty po-exec
i do polecenia;
lub+
tworzą polecenie do wykonania wraz z jego argumentami, z każdą instancją{}
argumentu zamienioną na bieżący plik (z;
) i{}
jako ostatni argument przed+
zastąpiony listą plików jako osobne argumenty (w{} +
przypadku). IOW-exec
przyjmuje kilka argumentów, zakończonych znakiem;
lub{}
+
.