Jak przenieść 100 plików z folderu zawierającego tysiące?

43

Mam katalog z tysiącami plików. Jak mogę przenieść 100 plików (wystarczy dowolny plik) do innej lokalizacji.

Gaijin
źródło
Ponieważ Unix i Linux nie mają jednej dużej strony internetowej o swoich narzędziach, po prostu przechodzę do strony podobnej do about.comi innej strony z listą dostępnych opcji, których mogę ewentualnie użyć ... ale nic nie znalazłemtail
gaijin

Odpowiedzi:

37
for file in $(ls -p | grep -v / | tail -100)
do
mv $file /other/location
done

Zakłada, że nazwy plików nie zawierają spacje, nowej linii (przy założeniu, że wartość domyślna $IFS), symbole wieloznaczne ( ?, *, [) lub zacząć -.

ennuikiller
źródło
12
Zauważ, że to podejście działa tylko wtedy, gdy w nazwach plików nie ma znaków specjalnych (białe znaki, znaki niedrukowalne, ``). Zasadniczo nie analizuj danych wyjściowychls . I zawsze podwójne cytowanie podstawień parametrów i poleceń.
Gilles „SO- przestań być zły”
1
Która część stanowi 100 plików?
tshepang
3
Zaktualizuj mój poprzedni komentarz: Po przeczytaniu linku referencyjnego Gillesa nie analizuj danych wyjściowych ls , zauważyłem, że findbrakuje mojej komendy. Argument był w niewłaściwym miejscu, a ja dodałem puste nazwy plików. Jest to nieco długi wiersz, ale to wszystko, co mogę zrobić w komentarzu. Oto naprawiony fragment:find . -maxdepth 1 -type f \( ! -iname ".*" \) -print0 | while read -rd $'\0' file ; do mv -- "$file" /other/location/ ; done
Peter.O
@perer Tak jak należy zauważyć, że read -dopcja nie jest przenośna dla wszystkich powłok, ale jeśli mimo to używasz bash, -d ''powinna dać ci taki sam efekt jak -d $'\0'.
jw013,
FWIW, jeśli chcesz, aby działało to jako oneliner, dodaj miejsce, w ;którym znajduje się teraz każda nowa linia.
Patrick
37

Najłatwiej jest w Zsh:

mv -- *([1,100]) /other/location/

To przenosi pierwsze 100 nie ukrytych plików (dowolnego typu, zmień ([1,100])na tylko (.[1,100])dla zwykłych plików lub (^/[1,100])dla dowolnego typu poza katalogiem ) w kolejności leksykograficznej. Możesz wybrać inną kolejność sortowania za pomocą o kwalifikatora glob , np. Aby przenieść 100 najstarszych plików:

mv -- *(Om[1,100]) /other/location/

Z innymi pociskami możesz to zrobić w pętli z wczesnym wyjściem.

i=0
for x in *; do
  if [ "$i" = 100 ]; then break; fi
  mv -- "$x" /other/location/
  i=$((i+1))
done

Innym przenośnym sposobem byłoby zbudowanie listy plików i usunięcie wszystkich oprócz ostatnich 100 .

Gilles „SO- przestań być zły”
źródło
+1 za bezpieczne rozszerzanie pocisków. Byłby również bardziej czytelny przy operacji przyrostowej $(( i++ ))lub $[ i++ ]?
2
@hesse Niektóre powłoki nie implementują ++i --. Możesz pisać : $((i+=1))zamiast i=$((i+1)); Nie jestem przekonany, że jest bardziej czytelny.
Gilles „SO- przestań być zły”
1
:-D, właściwie edytowałem tę odpowiedź, myśląc, że to moja ... Przepraszam. Cofnij się, ponieważ to zmienia znaczenie.
Stéphane Chazelas,
@ StéphaneChazelas Zastanawiam się, dlaczego miałbyś wykluczyć katalogi i dowiązania symboliczne, pytanie nie mówiło o tym.
Gilles „SO- przestań być zły”
@Gilles, zaakceptowana odpowiedź ls -p | grep -v /ma również ostatnie pytanie, które tutaj się powtarza.
Stéphane Chazelas,
8

Jeśli nie używasz zsh:

set -- *
[ "$#" -le 100 ] || shift "$(($# - 100))"
mv -- "$@" /target/dir

Przesunąłby ostatni (w kolejności alfabetycznej) 100.

Stéphane Chazelas
źródło
3

Pomocny byłby następujący oneliner w powłoce.

foreach i (`findDirectory -type f --max-depth 1 | tail -100`); zrobić; {mv $ i Target_Directory}; gotowy
diham
źródło
1
Co to za skorupa?
phk
@phk, działałoby to zshnawet, gdyby na pierwszy rzut oka wyglądało to zupełnie obco zsh. Gilles pokazał znacznie prostszy sposób, aby to zrobić zsh. Nawet wtedy jest to bardziej wiarygodne niż obecnie akceptowana odpowiedź.
Stéphane Chazelas,
3

Poniższe działało dla mnie. Przepraszam, jeśli został opublikowany wcześniej, ale nie zobaczyłem go w szybkim skanie.

ls path/to/dir/containing/files/* | head -100 | xargs -I{} cp {} /Path/to/new/dir
Joe
źródło
1

mmv to wyjątkowe narzędzie, które pozwala także na masową zmianę nazw plików. (Musiałem sudo apt-get install mmvgo zainstalować na komputerze). Prosty przykład użycia: załóżmy, że masz katalog plików z rozszerzeniem .JPG, który chciałbyś zmienić na małe .jpg. Następujące polecenie załatwia sprawę:

mmv \*.JPG \#1.jpg

Ukośnik odwrotny służy do pokazania, że ​​zbliża się symbol wieloznaczny. * / JPG pasuje do wszystkiego z rozszerzeniem JPG. W części polecenia „do” numer 1 używa pasującego tekstu z pierwszego symbolu wieloznacznego do zmiany nazwy pliku. Oczywiście możesz umieścić inną ścieżkę przed numerem 1, aby również przenieść plik.

Pete
źródło
2
Byłoby bardziej korzystne, gdybyś podał, w jaki sposób faktycznie skorzystasz z narzędzia, które sugerujesz, aby osiągnąć cel.
Dason
1
dodano przykład użycia
Pete
1
#!/bin/bash
c=1; d=1; mkdir -p NEWDIR_${d}
for jpg_file in *.jpg
do
if [ $c -eq 100 ]
then
d=$(( d + 1 )); c=0; mkdir -p NEWDIR_${d}
fi
mv "$jpg_file" NEWDIR_${d}/
c=$(( c + 1 ))
done

wypróbuj ten kod

sok
źródło
0

Spróbuj tego:

find /source/directory -type f -maxdepth 1 -print | tail -100 | xargs -J % mv % /other/location/
Saumil
źródło
Jest to niepoprawne, przekazujesz trzy argumenty mv, z których ostatni (prawdopodobnie) nie jest katalogiem. I tak naprawdę nie odpowiada na pytanie - pytający chce przenieść określoną liczbę plików, a nie wszystkie.
Mat
Polecenie zaktualizowane.
Saumil
0

Przyszedłem tutaj, ale musiałem kopiować pliki w częściach (99 każda) od /DIR1do /DIR2. Wkleję tutaj skrypt, aby pomóc innym:

#!/bin/bash
# Thanks to <Jordan_U> @ #ubuntu
# 06 Dec 2014

i=0
copy_unit=98

for file in /DIR1/*; do
  cp "$file" /DIR2
  if [[ "$i" -ge "$copy_unit" ]]; then
    echo "Pausing, press enter to continue"
    read
    i=0
  fi
  ((i++))
done
Enissay
źródło
0

Poniższe polecenie działa, jeśli jesteś zainteresowany użyciem ls

$ ls -rt source/* | head -n100 | xargs cp -t destination

Jak to działa ??

  • ls -rt source/* - polecenie wyświetla wszystkie pliki ze ścieżką względną
  • head -n100 - pobiera pierwsze 100 plików
  • xargs cp -t destination - przenosi te pliki do folderu docelowego
Rohith Yeravothula
źródło
0

Jeśli chcesz być bezpieczny / obsługiwać nazwy plików ze spacjami, znakami nowej linii, cudzysłowami, ukośnikami odwrotnymi itp., Musisz użyć separatorów zakończonych znakiem zerowym:

find "$srcdir" -maxdepth 1 -type f -print0 | head -z -n 100 | xargs -0 -r -- mv -t "$destdir" --

EDIT2: UWAGA: jeśli nie masz head -z( niezależnie od przyczyny ) można zastąpić wyżej head -z -n 1000z tr '\0\n' '\n\0' | head -n 1000 | tr '\0\n' '\n\0'(lub zobaczyć inne sposoby )

-maxdepth 1uniknie szukania plików w podkatalogach $srcdir, więc jedynymi wymienionymi są pliki wewnątrz $srcdir.
-print0użyje \0zamiast newline ( \n) między każdym wymienionym plikiem - pomaga to obsługiwać pliki zawierające znaki nowej linii i spacje za pomocą xargs.
head -zbędzie liczyć \0zakończone (zamiast zakończone newline \n) linie jako linie. -n 100wyświetli tylko pierwsze znalezione 100pliki find.
Jeśli chcesz zobaczyć, które polecenie xargszostanie wykonane, dodaj -t(lub --verbose).
xargs -0„Elementy wejściowe są zakończone znakiem null ( \0) zamiast spacją, a cudzysłowy i ukośnik odwrotny nie są wyjątkowe (każdy znak jest traktowany dosłownie)”
xargs -rnie będzie działaćmvjeśli nie ma żadnych plików do przeniesienia (tj. jeśli findnie znalazł żadnych plików).
--kończy przetwarzanie argumentów jako opcji programu, więcej szczegółów tutaj

Przykładowe dane wyjściowe (uruchamia jedno mvpolecenie i może również obsługiwać pliki z nową linią w nazwie):

$ find /tmp/t -maxdepth 1 -type f -print0 | head -z -n 100 | xargs -t -0 -r -- mv -t /tmp -- ; echo "exit codes: ${PIPESTATUS[@]}"
mv -t /tmp -- /tmp/t/file containing quotes"' then spaces /tmp/t/file containing quotes"' /tmp/t/file containing a slash n here\n /tmp/t/file containing a new line here
and continues /tmp/t/s /tmp/t/-x and -L 1. /tmp/t/of replace-str in the initi /tmp/t/-thisfile_starts_with_a_hyphen and has spaces and a -hyphen here /tmp/t/-thisfile_starts_with_a_hyphen and has spaces /tmp/t/-thisfile_starts_with_a_hyphen /tmp/t/another      with       spaces /tmp/t/one with spaces /tmp/t/c /tmp/t/a 
exit codes: 0 0 0

$ ls -1R /tmp/t
/tmp/t:
a
'another      with       spaces'
b
c
'file containing a new line here'$'\n''and continues'
'file containing a slash n here\n'
'file containing quotes"'\'''
'file containing quotes"'\'' then spaces'
'of replace-str in the initi'
'one with spaces'
s
'some dir'
-thisfile_starts_with_a_hyphen
'-thisfile_starts_with_a_hyphen and has spaces'
'-thisfile_starts_with_a_hyphen and has spaces and a -hyphen here'
'-x and -L 1.'

/tmp/t/b:
'file with spaces'

'/tmp/t/some dir':
'some file'

Dla find:

-maxdepth levels
       Descend at most levels (a non-negative integer) levels of direc‐
       tories below the starting-points.  -maxdepth 0
        means only apply the tests and actions to  the  starting-points
       themselves.
-type c
       File is of type c:

       b      block (buffered) special

       c      character (unbuffered) special

       d      directory

       p      named pipe (FIFO)

       f      regular file

       l      symbolic link; this is never true if the -L option or the
              -follow  option is in effect, unless the symbolic link is
              broken.  If you want to search for symbolic links when -L
              is in effect, use -xtype.

       s      socket

       D      door (Solaris)
-P     Never follow symbolic links.  This  is  the  default  behaviour.
       When find examines or prints information a file, and the file is
       a symbolic link, the information used shall be  taken  from  the
       properties of the symbolic link itself.
-L     Follow symbolic links.  When find examines or prints information
       about files, the information used shall be taken from the  prop‐
       erties  of  the file to which the link points, not from the link
       itself (unless it is a broken symbolic link or find is unable to
       examine  the file to which the link points).  Use of this option
       implies -noleaf.  If you later use the -P option,  -noleaf  will
       still  be  in  effect.   If -L is in effect and find discovers a
       symbolic link to a subdirectory during its search, the subdirec‐
       tory pointed to by the symbolic link will be searched.

       When the -L option is in effect, the -type predicate will always
       match against the type of the file that a symbolic  link  points
       to rather than the link itself (unless the symbolic link is bro‐
       ken).  Actions that can cause symbolic links  to  become  broken
       while  find  is executing (for example -delete) can give rise to
       confusing behaviour.  Using -L causes  the  -lname  and  -ilname
       predicates always to return false.

Dla head:

-n, --lines=[-]NUM
       print the first NUM lines instead of  the  first  10;  with  the
       leading '-', print all but the last NUM lines of each file
-z, --zero-terminated
       line delimiter is NUL, not newline

EDYCJA: Ktoś wspomniał , że nie ma head -z, to jest wersja, której używałem (w Fedorze 25):

$ head --version
head (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie and Jim Meyering.

$ rpm -qf /usr/bin/head
coreutils-8.25-17.fc25.x86_64

Dla xargs:

-0, --null
       Input  items  are  terminated  by a null character instead of by
       whitespace, and the quotes and backslash are not special  (every
       character is taken literally).  Disables the end of file string,
       which is treated like any other  argument.   Useful  when  input
       items  might  contain  white space, quote marks, or backslashes.
       The GNU find -print0 option produces  input  suitable  for  this
       mode.
-r, --no-run-if-empty
       If the standard input does not contain any nonblanks, do not run
       the command.  Normally, the command is run once even if there is
       no input.  This option is a GNU extension.
-P max-procs, --max-procs=max-procs
       Run  up  to max-procs processes at a time; the default is 1.  If
       max-procs is 0, xargs will run as many processes as possible  at
       a  time.   Use the -n option or the -L option with -P; otherwise
       chances are that only one exec will be  done.   While  xargs  is
       running,  you  can send its process a SIGUSR1 signal to increase
       the number of commands to run simultaneously, or  a  SIGUSR2  to
       decrease  the number.  You cannot increase it above an implemen‐
       tation-defined limit (which is shown with  --show-limits).   You
       cannot  decrease  it  below  1.  xargs never terminates its com‐
       mands; when asked to decrease, it merely waits for more than one
       existing command to terminate before starting another.

       Please  note  that  it is up to the called processes to properly
       manage parallel access to shared  resources.   For  example,  if
       more  than one of them tries to print to stdout, the ouptut will
       be produced in an indeterminate order (and very likely mixed up)
       unless  the  processes  collaborate in some way to prevent this.
       Using some kind of locking scheme is one  way  to  prevent  such
       problems.   In  general, using a locking scheme will help ensure
       correct output but reduce performance.  If  you  don't  want  to
       tolerate  the  performance  difference,  simply arrange for each
       process to produce a separate output file (or otherwise use sep‐
       arate resources).
-t, --verbose
       Print  the command line on the standard error output before exe‐
       cuting it.

Dla cp:

-t, --target-directory=DIRECTORY
       copy all SOURCE arguments into DIRECTORY
-v, --verbose
       explain what is being done

źródło
-1

Wiem, że ten wątek jest dość stary, ale znalazłem odpowiedzi bardziej skomplikowane, niż się spodziewałem. Działa to w CentOS, ale wydaje się na tyle proste, że prawdopodobnie powinno działać w innych dystrybucjach.

cp `ls someDir | head -n 100` someDir100/
Jason
źródło
1
To nie działa, ponieważ wynik lsnie zawiera wiodącego somedir/przedrostka i nie będzie działać w przypadku nazw plików ze znakami pustymi lub znakami wieloznacznymi lub na początku -.
Stéphane Chazelas
Słusznie. Właściwie zrobiłem cp ls | head -n 100../someDir100/ Z wnętrza katalogu docelowego i żadna z nazw plików nie spełniała tych przypadków. Lepiej mieć szczęście, niż dobrze!
Jason