Dlaczego find -exec mv {} ./target/ + nie działa?

98

Chcę dokładnie wiedzieć, co {} \;i {} \+i | xargs ...zrobić. Prosimy o wyjaśnienie ich wraz z objaśnieniami.

Poniżej 3 polecenia uruchamiają się i wyświetlają ten sam wynik, ale pierwsze polecenie zajmuje trochę czasu, a format jest również trochę inny.

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

To dlatego, że 1st uruchamia filepolecenie dla każdego pliku pochodzącego z findpolecenia. Więc w zasadzie działa jako:

file file1.txt
file file2.txt

Ale ostatnie 2 znajdź za pomocą -execpoleceń uruchom polecenie pliku raz dla wszystkich plików, jak poniżej:

file file1.txt file2.txt

Następnie uruchamiam następujące polecenia, na których pierwsza działa bez problemu, a druga wyświetla komunikat o błędzie.

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

W przypadku polecenia z {} \+daje mi komunikat o błędzie

find: missing argument to `-exec'

dlaczego? czy ktoś może wyjaśnić, co robię źle?

Shahadat Hossain
źródło
prawdziwe pytanie jest proste, dlaczego pierwsza działa, a druga nie? (1) znaleźć. -type f -iname ' .cpp' -exec mv {} ./test/ \; (2) znaleźć. -type f -iname ' .cpp' -exec mv {} ./test/ \ +
Shahadat Hossain

Odpowiedzi:

185

Strona podręcznika (lub internetowy podręcznik GNU ) prawie wszystko wyjaśnia.

polecenie find -exec {} \;

Dla każdego wyniku command {}jest wykonywany. Wszystkie wystąpienia {}są zastępowane nazwą pliku. ;jest poprzedzony ukośnikiem, aby zapobiec jego interpretacji przez powłokę.

znajdź -exec polecenie {} +

Każdy wynik jest następnie dołączany commandi wykonywany. Biorąc pod uwagę ograniczenia długości polecenia, myślę, że to polecenie może być wykonywane więcej razy, przy wsparciu strony podręcznika:

całkowita liczba wywołań polecenia będzie znacznie mniejsza niż liczba dopasowanych plików.

Zwróć uwagę na ten cytat ze strony podręcznika:

Linia poleceń jest zbudowana w podobny sposób, w jaki xargs buduje swoje linie poleceń

Dlatego żadne znaki nie są dozwolone między {}i +z wyjątkiem białych znaków. +powoduje, że find wykryje, że argumenty powinny być dołączone do polecenia tak samo, jak xargs.

Rozwiązanie

Na szczęście implementacja GNU mvakceptuje katalog docelowy jako argument, z jednym -tlub dłuższym parametrem --target. Jego użycie będzie:

mv -t target file1 file2 ...

Twoje findpolecenie staje się:

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

Ze strony podręcznika:

-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 (za pomocą `\ ') lub zacytowania, aby chronić je przed rozszerzaniem przez powłokę. Zobacz sekcję PRZYKŁADY, 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życiem akcji -exec; powinieneś zamiast tego użyć opcji -execdir.

-exec polecenie {} +

Ten wariant akcji -exec uruchamia określone polecenie na wybranych plikach, ale wiersz poleceń jest tworzony przez dołączanie na końcu każdej wybranej nazwy pliku; 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.

Lekensteyn
źródło
1
Wiem, jak to działa, przeglądałem tę instrukcję kilka razy, ale pojawił się komunikat o błędzie dotyczący używania {} +, chociaż działa dla {} \; i używam Cygwin w systemie Windows.
Shahadat Hossain,
1
@Shahadat: czy przeczytałeś część przed „Ze strony podręcznika”? Wstawiłeś ./test/między {}i +, ale między nimi nie są dozwolone żadne znaki inne niż białe znaki.
Lekensteyn
ru mówiąc, że nie powinienem umieszczać ./test/ pomiędzy {} a +. Wtedy jak będzie działać polecenie mv; mv potrzebuje źródła, którym jest {} i miejsca docelowego, którym jest ./test/ i zakończenia z +. czy możesz napisać polecenie, co myślisz, prawda?
Shahadat Hossain
@Shahadat: Widzę, co próbujesz osiągnąć. System Windows wolno uruchamia programy, więc chcesz połączyć to w jedno polecenie. Dodam alternatywę do odpowiedzi.
Lekensteyn
1
+Komenda jest nieco dziwne AFAIU ponieważ kije pliki na „koniec” (a nie w miejscu {}), dzięki czemu korzystając {}w ogóle - jest to mylące. Dzięki za -topcję, o której nie wiedziałem, wygląda na to, że opcja została utworzona jako obejście tego -exec +problemu!
e2-e4
6

Napotkałem ten sam problem na Mac OSX , używając powłoki ZSH : w tym przypadku nie ma -topcji mv, więc musiałem znaleźć inne rozwiązanie. Jednak następujące polecenie powiodło się:

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

Sekret polegał na zacytowaniu szelek . Nie ma potrzeby, aby klamry znajdowały się na końcu execpolecenia.

Testowałem pod Ubuntu 14.04 (z powłokami BASH i ZSH ), działa tak samo.

Jednak podczas używania +znaku rzeczywiście wydaje się, że musi on znajdować się na końcu execpolecenia.

arvymetal
źródło
{}musi być cytowany w fishi rcmuszle, ale nie zsh, bashani żadnych innych powłok Bourne lub csh rodzin.
Stephane Chazelas
@StephaneChazelas Tak, ponownie przetestowane pod Ubuntu z bash, rzeczywiście cytaty nie są konieczne. Co ciekawe, miałem problem, jeśli nie cytowałem ich pod MacOS (używając zsh). Ale nie mam Maca w zasięgu ręki, żeby spróbować ponownie ...
arvymetal
3

Standardowym odpowiednikiem find -iname ... -exec mv -t dest {} +dla findimplementacji, które nie obsługują -inamelub mvimplementacji, które nie obsługują, -tjest użycie powłoki do zmiany kolejności argumentów:

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

Używając -name '*.[cC][pP][pP]', unikamy również polegania na bieżących ustawieniach regionalnych przy decydowaniu, jaka jest wersja club wielka litera p.

Zauważ, że +w przeciwieństwie do ;nie jest wyjątkowy w żadnej powłoce, więc nie musi być cytowany (chociaż cytowanie nie zaszkodzi, z wyjątkiem oczywiście takich powłok rc, które nie obsługują \operatora cytowania).

Kropka /w /dest/dir/jest tak, że mvnie powiedzie się z powodu błędu zamiast zmiany nazwy foo.cppdo /dest/dirw przypadku, gdy tylko jeden cppplik został odnaleziony i /dest/dirnie istnieje lub nie jest katalogiem (lub dowiązaniem do katalogu).

Stephane Chazelas
źródło
+1 ... operacja w powłoce jako wstęp do wykonania polecenia jest w rzeczywistości przydatna w różnych przypadkach użycia ... fajnie.
Cbhihe
0
find . -name "*.mp3" -exec mv --target-directory=/home/d0k/Музика/ {} \+
DarkLabs
źródło
Dodaj wyjaśnienie do swojej odpowiedzi, aby inni mogli się z niej nauczyć
Nico Haase
Musisz odpowiedzieć na pytanie, które poprosiło o wyjaśnienie. Tylko kod nie jest odpowiedzią.
Lajos Arpad
-1

nie, różnica między +i \;powinna zostać odwrócona. +dołącza pliki na koniec polecenia exec, a następnie uruchamia polecenie exec i \;uruchamia polecenie dla każdego pliku.

Problem w tym, find . -type f -iname '*.cpp' -exec mv {} ./test/ \+że find . -type f -iname '*.cpp' -exec mv {} ./test/ + nie powinno być potrzeby ucieczki lub kończenia rozszerzenia+

xargs, których nie używałem od dłuższego czasu, ale myślę, że działa jak +.

Mike Ramirez
źródło
Próbowałem też z tym, ale otrzymałem ten sam komunikat o błędzie. Co więcej, wszędzie, gdzie znalazłem używanie tylko +, ale w moim cygwinie muszę używać \ + lub "+" do pracy.
Shahadat Hossain,
och, to jest środowisko cygwinów. Przepraszam, ale nie wiem, nie używam powłoki cygwin, po prostu używam * nix.
Mike Ramirez,
1
@Shahadat Hossain spróbować -name "*.cpp"ja prawie nie używa -iname chyba że chcesz zrobić trochę trudne szukanie regex, jak -iname '??? pracy * \ CPP..'
Mike Ramirez
1
@Mike: Myślę, że źle zrozumiałeś różnicę między -inamei -name. -inamejest wersją programu -namebez rozróżniania wielkości liter i nie ma różnic w obsłudze wyrażeń regularnych. Proponuję wypróbować polecenia przed wysłaniem, twoje polecenie również nie działa w mojej powłoce.
Lekensteyn
1
@Lekensteyn Ustalono już, że tak było przed Twoim komentarzem. Myślałem, że poznałem Shahadat przed twoim postem, to było proste "ok". Nie, nie uruchamiałem tego ręcznie, robiłem to od czubka głowy i rzadko używam tej formy wyszukiwania wyrażeń regularnych za pomocą find. To była po prostu rzecz typu „może pomóc”.
Mike Ramirez