¿Cómo mover 100 archivos de una carpeta que contiene miles?

43

Tengo un directorio con miles de archivos. ¿Cómo puedo mover 100 de los archivos (cualquier archivo lo hará) a otra ubicación?

gaijin
fuente
Debido a que Unix y Linux no tienen un gran sitio web sobre sus herramientas, simplemente voy al sitio web como about.comy a otro sitio web para obtener la lista de opciones disponibles que posiblemente puedo usar ... pero no encontré nada comotail
gaijin

Respuestas:

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

Esto supone nombres de los archivos no contienen espacios en blanco, de nueva línea (suponiendo que el valor por defecto de $IFS), los caracteres comodín ( ?, *, [) o empezar con -.

ennuikiller
fuente
12
Tenga en cuenta que este enfoque solo funciona si no hay caracteres especiales (espacios en blanco, caracteres no imprimibles, ``) en los nombres de archivo. Como regla general, no analice la salida dels . Y siempre entre comillas dobles de parámetros y sustituciones de comandos.
Gilles 'SO- deja de ser malvado'
1
¿Qué parte representa 100 archivos?
tshepang
3
Actualización de mi comentario anterior: después de leer el enlace de referencia de Gilles, no analice la salida de ls , descubrí que findfaltaba mi comando. Un argumento estaba en el lugar equivocado, y he agregado terminaciones de nombre de archivo nulo. Es un poco largo de una sola línea, pero eso es todo lo que puedo hacer en un comentario. Aquí está el fragmento fijo:find . -maxdepth 1 -type f \( ! -iname ".*" \) -print0 | while read -rd $'\0' file ; do mv -- "$file" /other/location/ ; done
Peter
@perer Solo como una nota de que la read -dopción no es portátil para todos los shells, pero si está usando de bashtodos modos, -d ''debería obtener el mismo efecto que -d $'\0'.
jw013
FWIW, si desea que esto se ejecute como una línea, agregue un lugar ;donde está cada nueva línea ahora.
patrick
37

Es más fácil en zsh:

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

Esto mueve los primeros 100 archivos no ocultos (de cualquier tipo, cambie ([1,100])a solo (.[1,100])para archivos normales , o (^/[1,100])para cualquier tipo que no sea directorio ) en orden lexicográfico de nombre. Puede seleccionar un orden de clasificación diferente con el o calificador global , por ejemplo, para mover los 100 archivos más antiguos:

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

Con otros proyectiles, puede hacerlo en un bucle con una salida anticipada.

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

Otra forma portátil sería construir la lista de archivos y eliminar todos menos los últimos 100 .

Gilles 'SO- deja de ser malvado'
fuente
+1 para una expansión segura de la carcasa. También sería más legible con la operación de incremento $(( i++ ))o $[ i++ ]?
2
@hesse Algunos shells no se implementan ++y --. Puedes escribir en : $((i+=1))lugar de i=$((i+1)); No estoy convencido de que sea más legible.
Gilles 'SO- deja de ser malvado'
1
:-D, en realidad edité esta respuesta pensando que era mía ... Lo siento. Siéntase libre de revertir ya que eso cambia el significado.
Stéphane Chazelas
@ StéphaneChazelas Me pregunto por qué excluirías directorios y enlaces simbólicos, la pregunta no decía nada al respecto.
Gilles 'SO- deja de ser malvado'
@Gilles, la respuesta aceptada ls -p | grep -v /también tiene la pregunta reciente que se duplica aquí.
Stéphane Chazelas
8

Si no estás usando zsh:

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

Movería los últimos (en orden alfabético) 100 unidades.

Stéphane Chazelas
fuente
4

Lo siguiente funcionó para mí. Lo siento si se publicó anteriormente, pero no lo vi en un escaneo rápido.

ls path/to/dir/containing/files/* | head -100 | xargs -I{} cp {} /Path/to/new/dir
Joe
fuente
3

El siguiente oneliner en shell ayudaría.

foreach i (`find Source_Directory -type f --max-depth 1 | tail -100`); hacer; {mv $ i Target_Directory}; hecho
diham
fuente
1
¿Qué es esa concha?
phk
@phk, eso funcionaría zshincluso si a primera vista parece bastante ajeno a la zshsintaxis. Gilles ha mostrado una forma mucho más simple de hacerlo zsh. Incluso entonces, eso es aún más confiable que la respuesta actualmente aceptada.
Stéphane Chazelas
1

mmv es una utilidad excepcional que también te permitirá cambiar el nombre de los archivos en masa. (Tuve sudo apt-get install mmvque instalarlo en mi computadora). Ejemplo de uso simple: suponga que tiene un directorio de archivos con extensión .JPG que desea cambiar a minúscula .jpg. El siguiente comando hace el truco:

mmv \*.JPG \#1.jpg

La barra invertida se usa para mostrar que se está acercando un comodín. El * / JPG coincide con cualquier cosa con una extensión JPG. En la parte "a" del comando, el # 1 usa el texto correspondiente del primer comodín para cambiar el nombre del archivo. Por supuesto, puede poner una ruta diferente antes del # 1 para mover también el archivo.

Pete
fuente
2
Sería más beneficioso si proporcionara cómo utilizaría realmente la herramienta que sugiere para lograr el objetivo.
Dason
1
se agregó un ejemplo de uso
Pete el
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

prueba este código

jugo
fuente
1

el siguiente comando funciona, si está interesado en usar ls

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

Como funciona esto ??

  • ls -rt source/* - el comando enumera todos los archivos con la ruta relativa
  • head -n100 - toma los primeros 100 archivos
  • xargs cp -t destination - mueve estos archivos a la carpeta de destino
Rohith Yeravothula
fuente
0

Prueba esto:

find /source/directory -type f -maxdepth 1 -print | tail -100 | xargs -J % mv % /other/location/
Saumil
fuente
Esto es incorrecto, le está pasando tres argumentos mv, el último de los cuales (probablemente) no es un directorio. Y en realidad no responde a la pregunta: el autor de la pregunta quiere mover un número determinado de archivos, no todos.
Mat
Comando actualizado.
Saumil
0

Vine aquí, pero necesitaba copiar archivos en partes (99 cada uno) de /DIR1a /DIR2. Pegaré el script aquí para ayudar a otherz tal vez:

#!/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
fuente
0

Si desea estar seguro / manejar nombres de archivos con espacios, líneas nuevas, comillas, barras invertidas, etc., debe usar separadores con terminación nula:

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

EDIT2: NOTA: si no tiene head -z( por alguna razón ) puede reemplazar lo anterior head -z -n 1000con tr '\0\n' '\n\0' | head -n 1000 | tr '\0\n' '\n\0'(o ver otras formas )

-maxdepth 1evitará buscar archivos en subdirectorios de $srcdir, por lo que los únicos enumerados son los archivos dentro $srcdir.
-print0usará en \0lugar de nueva línea ( \n) entre cada archivo de la lista; esto ayuda a manejar archivos que contienen nuevas líneas y espacios con xargs.
head -zcontará las líneas \0terminadas (en lugar de newline ( \n) terminadas) como líneas. -n 100enumerará solo los primeros 100archivos que findencontró.
Si desea ver qué comando xargsse ejecutará, agregue -t(o --verbose).
xargs -0"Los elementos de entrada terminan con un carácter nulo ( \0) en lugar de un espacio en blanco, y las comillas y la barra diagonal inversa no son especiales (cada carácter se toma literalmente)"
xargs -rno se ejecutarámvsi no hay archivos para mover (es decir, si findno encontró ningún archivo).
--termina el procesamiento de argumentos como opciones para el programa, más detalles aquí

Salida de muestra (ejecuta un mvcomando y también puede manejar archivos con nuevas líneas en su nombre):

$ 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'

Para 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.

Para 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

EDITAR: Alguien mencionó que no tenían head -z, esta es la versión que estaba usando (en Fedora 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

Para 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.

Para cp:

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

fuente
-2

Sé que este hilo es bastante antiguo, pero encontré las respuestas más complicadas de lo que pensé que deberían ser. Esto funcionó en CentOS, pero parece bastante simple que probablemente debería funcionar en otras distribuciones.

cp `ls someDir | head -n 100` someDir100/
Jason
fuente
1
Eso no funciona porque la salida de lsno incluirá el somedir/prefijo inicial, y no funcionará para el nombre de archivo con caracteres en blanco o comodín ni comenzará con -.
Stéphane Chazelas
Lo suficientemente justo. Realmente hice cp ls | head -n 100../someDir100/ Desde dentro del directorio de destino y ninguno de los nombres de archivo satisfizo esos casos. ¡Mejor tener suerte que bien!
Jason