Jak zintegrować komendę mv po komendzie find?


60

Szukam plików, których nazwy zawierają się AAAw ich ścieżce za pomocą następującego polecenia:

find path_A -name "*AAA*"

Biorąc pod uwagę wynik pokazany przez powyższe polecenie, chcę przenieść te pliki na inną ścieżkę, powiedzmy path_B. Czy zamiast przenosić te pliki jeden po drugim, czy mogę zoptymalizować polecenie, przenosząc te pliki zaraz po poleceniu find?

Odpowiedzi:


86

Z GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

To użyje -execopcji find, która po kolei zamienia {}z każdym wynikiem find i uruchamia podane polecenie. Jak wyjaśniono w man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

W takim przypadku korzystamy z +wersji, -execaby mvwykonać jak najmniej operacji:

   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.

Nie zapomnij o „\;” na końcu exec!
Charles Roth,

@Charles Roth: to jest +praca, możesz przeczytać mój cytat powyżej lub man findzamiast
cuonglm

Dzięki za to! Próbowałem użyć -exec mv {} path_b +i nie udało się z błędami uprawnień. TBH, wciąż nie rozumiem dlaczego, ale -exec mv -t path_b {} +działa uczta!
Jeremy Davis,

6
@JeremyDavis Podczas korzystania -exec ... {} +The {}musi być ostatnią rzeczą przed +. Dlatego używa, mv -t destdir {} +a nie używa mv {} destdir +. -exec mv {} destdir ';'Zamiast tego zastosowano jedno zimno , ale wykonałoby się to mvraz dla każdego pliku.
Kusalananda

23

Możesz również zrobić coś takiego jak poniżej.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Gdzie,

  1. -0Jeśli są puste miejsca lub znaki (w tym znaki nowej linii), wiele poleceń nie będzie działać. Ta opcja obsługuje nazwy plików z pustą przestrzenią.
  2. -IZamień wystąpienie replace-str w argumentach początkowych na nazwy odczytane ze standardowego wejścia. Ponadto, niecytowane puste miejsca nie kończą elementów wejściowych; zamiast tego separatorem jest znak nowej linii.

Testowanie

Utworzyłem dwa katalogi jako sourcediri destdir. Teraz, stworzyłem kilka plików wewnątrz sourcedirjak file1.bak, file2.bakifile3 with spaces.bak

Teraz wykonałem polecenie jako

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Teraz, w środku destdir, kiedy nie lsmogłem zobaczyć, że pliki zostały przeniesione od sourcedirdo destdir.

Bibliografia

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/


22

Z korzyścią dla użytkowników OS X napotykających to pytanie, składnia w OS X jest nieco inna. Zakładając, że nie chcesz wyszukiwać rekurencyjnie w podkatalogach path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Jeśli chcesz przeszukać rekursywnie wszystkie pliki w path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;

To nie jest składnia specyficzna dla OS X, jest taka sama z każdą, findktórej użyłem. -maxdepthZalety : (szczególnie jeśli ścieżka_B jest podkatalogiem - unika mvprób przenoszenia plików już tam znajdujących się!) I używania \; (więc {} nie musi być ostatnim parametrem i mvmożna użyć normalnej składni)
drevicko

6

To -execnajlepszy sposób, aby to zrobić. Jeśli z jakiegokolwiek powodu nie jest to opcja, możesz również odczytać wyniki w pętli:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Jest to bezpieczny sposób, może poradzić sobie z nazwami plików, które zawierają spacje, znaki nowej linii lub inne dziwne znaki. Prostszy sposób, ale taki, który zawiedzie, chyba że nazwy plików składają się tylko z prostych znaków alfanumerycznych , to

mv $(find path_A -name "*AAA*") path_B

Ale użyj pętli while.


Prostszy sposób może się jeszcze nie powieść, jeśli your file names consist only of simple alphanumeric charactersnp. ARG_MAX


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.