Come spostare 100 file da una cartella contenente migliaia?


43

Ho una directory con migliaia di file. Come posso spostare 100 dei file (qualsiasi file farà) in un'altra posizione.


Poiché Unix e Linux non hanno un grande sito Web sui suoi strumenti, quindi vado semplicemente al sito Web come about.come ad altri siti Web per l'elenco delle opzioni disponibili che posso eventualmente usare .. ma non ho trovato nulla di similetail
gaijin

Risposte:


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

Che assume i nomi dei file non contengono spazi vuoti, di nuova riga (assumendo il valore di default $IFS), i caratteri jolly ( ?, *, [) oppure iniziano con -.


12
Si noti che questo approccio funziona solo se non ci sono caratteri speciali (spazi bianchi, caratteri non stampabili, ``) nei nomi dei file. Come regola generale, non analizzare l'output dils . E sempre virgolette doppie e sostituzioni di comandi.
Gilles 'SO- smetti di essere malvagio'

1
Quale parte rappresenta 100 file?
Tshepang,

3
Aggiornamento al mio commento precedente: dopo aver letto il link di riferimento di Gilles, non analizzare l'output di ls , ho scoperto che il mio findcomando mancava. Un arg era nel posto sbagliato e ho aggiunto terminazioni di nomi di file null. È un po 'lungo di una riga, ma è tutto ciò che posso fare in un commento. Ecco lo snippet fisso:find . -maxdepth 1 -type f \( ! -iname ".*" \) -print0 | while read -rd $'\0' file ; do mv -- "$file" /other/location/ ; done
Peter.O

@perer Solo come nota che l' read -dopzione non è portabile su tutte le shell, ma se stai usando bashcomunque, -d ''dovresti ottenere lo stesso effetto di -d $'\0'.
jw013,

FWIW se vuoi che funzioni come oneliner, aggiungi un punto in ;cui ogni nuova riga è ora.
patrick

37

È più semplice in zsh:

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

Ciò sposta i primi 100 file non nascosti (di qualsiasi tipo, cambiati ([1,100])in solo (.[1,100])per file normali o (^/[1,100])per qualsiasi tipo tranne directory ) nell'ordine lessicografico dei nomi. È possibile selezionare un diverso ordinamento con il o qualificatore glob , ad esempio per spostare i 100 file più vecchi:

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

Con altre shell, puoi farlo in un ciclo con un'uscita anticipata.

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

Un altro modo portatile sarebbe quello di costruire l'elenco dei file e rimuovere tutti tranne gli ultimi 100 .


+1 per l'espansione sicura della shell. Sarebbe anche più leggibile con l'operazione di incremento $(( i++ ))o $[ i++ ]?

2
@hesse Alcune shell non implementano ++e --. Puoi scrivere : $((i+=1))invece di i=$((i+1)); Non sono convinto che sia più leggibile.
Gilles 'SO- smetti di essere malvagio' il

1
MrGreen, in realtà ho modificato questa risposta pensando che fosse mia ... Scusa. Sentiti libero di tornare mentre questo cambia il significato.
Stéphane Chazelas,

@ StéphaneChazelas Mi chiedo perché dovresti escludere directory e collegamenti simbolici, la domanda non ha detto nulla al riguardo.
Gilles 'SO- smetti di essere malvagio' il

@Gilles, la risposta accettata ls -p | grep -v /ha anche la recente domanda che duplica qui.
Stéphane Chazelas,

8

Se non stai usando zsh:

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

Sposta gli ultimi (in ordine alfabetico) di 100.


4

Quanto segue ha funzionato per me. Scusate se è stato pubblicato in precedenza, ma non l'ho visto in una scansione rapida.

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

3

Il seguente oneliner in shell sarebbe di aiuto.

foreach i (`find Source_Directory -type f --max-depth 1 | tail -100`); fare; {mv $ i Target_Directory}; fatto

1
Cos'è quella conchiglia?
phk,

@phk, ciò accadrà zshanche se a prima vista sembra abbastanza estraneo alla zshsintassi. Gilles ha mostrato un modo molto più semplice di farlo zsh. Anche allora, è ancora più affidabile della risposta attualmente accettata.
Stéphane Chazelas,


1

mmv è un'utilità eccezionale che ti permetterà anche di rinominare in massa i file. (Ho dovuto sudo apt-get install mmvinstallarlo sul mio computer.) Esempio di semplice utilizzo: supponiamo di avere una directory di file con estensione .JPG che vorresti cambiare in un minuscolo .jpg. Il seguente comando fa il trucco:

mmv \*.JPG \#1.jpg

La barra rovesciata viene utilizzata per mostrare che sta arrivando un carattere jolly. * / JPG corrisponde a qualsiasi cosa con un'estensione JPG. Nella parte "a" del comando, il numero 1 utilizza il testo corrispondente dal primo carattere jolly per rinominare il file. Ovviamente, puoi inserire un percorso diverso prima del n. 1 per spostare anche il file.


2
Sarebbe più utile se fornissi come utilizzeresti effettivamente lo strumento che suggerisci per raggiungere l'obiettivo.
Dason,

1
aggiunto un esempio di utilizzo
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

prova questo codice


1

il seguente comando funziona, se sei interessato ad usare ls

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

Come funziona ??

  • ls -rt source/* - Il comando elenca tutti i file con il relativo percorso
  • head -n100 - prende i primi 100 file
  • xargs cp -t destination - sposta questi file nella cartella di destinazione

0

Prova questo:

find /source/directory -type f -maxdepth 1 -print | tail -100 | xargs -J % mv % /other/location/

Questo non è corretto, stai passando tre argomenti mv, l'ultimo dei quali (probabilmente) non è una directory. E non risponde davvero alla domanda: chi lo desidera desidera spostare un determinato numero di file, non tutti.
Mat

Comando aggiornato.
Saumil,

0

Sono venuto qui, ma avevo bisogno di copiare i file in parti (99 ciascuno) da /DIR1a /DIR2. Incollerò qui lo script per aiutare gli altri forse:

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

0

Se vuoi essere sicuro / gestire nomi di file con spazi, newline, virgolette, barre rovesciate ecc., Devi usare separatori con terminazione null:

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

EDIT2: NOTA: se non hai head -z( per qualsiasi motivo ) puoi sostituire quanto sopra head -z -n 1000con tr '\0\n' '\n\0' | head -n 1000 | tr '\0\n' '\n\0'(o vedere altri modi )

-maxdepth 1eviterà di cercare file nelle sottodirectory di $srcdir, quindi gli unici elencati sono i file all'interno $srcdir.
-print0utilizzerà al \0posto di newline ( \n) tra ogni file elencato - questo aiuta a gestire i file contenenti newline e spazi con xargs.
head -zconterà le linee \0terminate (anziché newline ( \n) terminate) come linee. -n 100elencherà solo i primi 100file findtrovati.
Se vuoi vedere quale comando xargsverrà eseguito, aggiungi -t(o --verbose).
xargs -0"Gli elementi di input sono terminati da un carattere null ( \0) anziché da spazi bianchi e le virgolette e la barra rovesciata non sono speciali (ogni carattere è preso alla lettera)"
xargs -rnon verrà eseguitomvse non ci sono file da spostare (ad es. se findnon sono stati trovati file).
--termina l'elaborazione degli argomenti come opzioni per il programma, maggiori dettagli qui

Output di esempio (esegue un mvcomando e può gestire anche file con newline nel loro nome):

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

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

Per 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

EDIT: Qualcuno ha detto che non avevano head -z, questa è la versione che stavo usando (in 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

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

Per cp:

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


-2

So che questa discussione è piuttosto vecchia, ma ho trovato le risposte più complicate di quanto pensassi che dovessero essere. Questo ha funzionato in CentOS, ma sembra abbastanza semplice che probabilmente dovrebbe funzionare in altre distro.

cp `ls someDir | head -n 100` someDir100/

1
Ciò non funziona perché l'output di lsnon include il somedir/prefisso iniziale e non funziona per il nome file con caratteri vuoti o jolly o inizia con -.
Stéphane Chazelas,

Giusto. In realtà ho fatto cp ls | head -n 100../someDir100/ Dall'interno della directory di destinazione e nessuno dei nomi dei file ha soddisfatto quei casi. Meglio essere fortunati, allora bene!
Jason,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.