copiare prima i file più piccoli?


15

Ho una grande directory contenente sottodirectory e file che desidero copiare in modo ricorsivo.

C'è un modo per dire cpche dovrebbe eseguire l'operazione di copia in ordine di dimensione del file, in modo che i file più piccoli vengano copiati per primi?


1
Solo per essere sicuro che non ci sia un problema XY in questione, puoi spiegare perché vuoi farlo?
Riccioli d'oro

4
@ TAFKA'goldilocks '- Ho molti file video e vorrei testare la qualità di ogni directory. Il video più piccolo mi darà una rapida indicazione se anche il resto dei file è difettoso.
nbubis,

Risposte:


10

Questo fa tutto il lavoro in una volta sola - in tutte le directory secondarie, tutte in un singolo flusso senza problemi di nome file. Copia dal più piccolo al più grande ogni file che hai. Sarà necessario mkdir ${DESTINATION}se non esiste già.

find . ! -type d -print0 |
du -b0 --files0-from=/dev/stdin |
sort -zk1,1n | 
sed -zn 's/^[^0-9]*[0-9]*[^.]*//p' |
tar --hard-dereference --null -T /dev/stdin -cf - |
    tar -C"${DESTINATION}" --same-order -xvf -

Sai una cosa, però? Ciò che ciò non fa è svuotare le directory secondarie. Potrei fare un reindirizzamento su quella pipeline, ma è solo una condizione di gara in attesa di accadere. Il più semplice è probabilmente il migliore. Quindi fallo solo dopo:

find . -type d -printf 'mkdir -p "'"${DESTINATION}"'/%p"\n' |
    . /dev/stdin

Oppure, dal momento che Gilles fa un ottimo punto nella sua risposta per preservare i permessi delle directory, dovrei provare anche io. Penso che questo lo farà:

find . -type d -printf '[ -d "'"${DESTINATION}"'/%p" ] || 
    cp "%p" -t "'"${DESTINATION}"'"\n' |
. /dev/stdin

Sarei disposto a scommettere che è più veloce di mkdircomunque.


1
Accidenti, mikeserv! +1
Riccioli d'oro

3
@ TAFKA'goldilocks 'Lo prenderò come un complimento. Grazie mille.
Mikeserv,

15

Ecco un metodo rapido e sporco con rsync. Per questo esempio sto prendendo in considerazione qualcosa di meno di 10 MB come "piccolo".

Innanzitutto trasferisci solo i file di piccole dimensioni:

rsync -a --max-size=10m srcdir dstdir

Quindi trasferire i file rimanenti. I file di piccole dimensioni precedentemente trasferiti non verranno copiati a meno che non siano stati modificati.

rsync -a srcdir dstdir

A partire dal man 1 rsync

   --max-size=SIZE
          This  tells  rsync to avoid transferring any file that is larger
          than the specified SIZE. The SIZE value can be suffixed  with  a
          string  to  indicate  a size multiplier, and may be a fractional
          value (e.g. "--max-size=1.5m").

          This option is a transfer rule, not an exclude,  so  it  doesnt
          affect  the  data  that  goes  into  the file-lists, and thus it
          doesnt affect deletions.  It just limits  the  files  that  the
          receiver requests to be transferred.

          The  suffixes  are  as  follows:  "K"  (or  "KiB") is a kibibyte
          (1024), "M" (or "MiB") is a mebibyte (1024*1024),  and  "G"  (or
          "GiB")  is  a gibibyte (1024*1024*1024).  If you want the multi
          plier to be 1000 instead of  1024,  use  "KB",  "MB",  or  "GB".
          (Note: lower-case is also accepted for all values.)  Finally, if
          the suffix ends in either "+1" or "-1", the value will be offset
          by one byte in the indicated direction.

          Examples:    --max-size=1.5mb-1    is    1499999    bytes,   and
          --max-size=2g+1 is 2147483649 bytes.

Naturalmente, l'ordine di trasferimento file per file non è strettamente dal più piccolo al più grande, ma penso che possa essere la soluzione più semplice che soddisfa lo spirito delle tue esigenze.


Qui ottieni 2 copie di hard-link e i soft-link vengono trasformati in file effettivi per due copie di ciascuno. Faresti molto meglio con --copy-dest=DIRe / o --compare-dest=DIRpenso. So solo perche 'ho dovuto aggiungere --hard-dereferencea me stesso di tardopo la pubblicazione la mia risposta, perché mi mancava i link. Penso che in rsyncrealtà si comporti in modo più specifico con i file system locali con quegli altri - lo usavo con le chiavi USB e inondava il bus a meno che non imposti un limite di larghezza di banda. Penso che avrei dovuto usare uno di questi altri invece.
Mikeserv,

1
+1 per il "metodo rapido e sporco". Di solito è più semplice, almeno per scopi di automazione e manutenibilità futura. Penso che questo sia in realtà abbastanza pulito. "Elegante" vs "kludgy" e "robusto" vs "instabile" possono talvolta essere in conflitto come obiettivi di progettazione, ma esiste un buon equilibrio che può essere raggiunto e penso che sia elegante e abbastanza robusto.
Wildcard il

4

Non cpdirettamente, questo va ben oltre le sue capacità. Ma puoi organizzare di chiamare cpi file nel giusto ordine.

Zsh consente convenientemente di ordinare i file per dimensione con un qualificatore glob . Ecco uno snippet zsh che copia i file in ordine crescente di dimensioni da sotto /path/to/source-directorya sotto /path/to/destination-directory.

cd /path/to/source-directory
for x in **/*(.oL); do
  mkdir -p /path/to/destination-directory/$x:h
  cp $x /path/to/destination-directory/$x:h
done

Invece di un ciclo, è possibile utilizzare la zcpfunzione. Tuttavia, è necessario creare prima le directory di destinazione, che possono essere eseguite in un criptico oneliner.

autoload -U zmv; alias zcp='zmv -C'
cd /path/to/source-directory
mkdir **/*(/e\''REPLY=/path/to/destination-directory/$REPLY'\')
zcp -Q '**/*(.oL)' '/path/to/destination-directory/$f'

Ciò non preserva la proprietà delle directory di origine. Se lo desideri, dovrai arruolare un programma di copia adatto come cpioo pax. Se lo fai, non è necessario chiamare cpo zcpin aggiunta.

cd /path/to/source-directory
print -rN **/*(^.) **/*(.oL) | cpio -0 -p /path/to/destination-directory

2

Non penso che ci sia modo cp -rdi farlo direttamente. Dal momento che potrebbe essere un periodo di tempo indeterminato prima di ottenere una procedura guidata find/ magica awk, ecco un veloce script perl:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

use File::Find;
use File::Basename;

die "No (valid) source directory path given.\n"
    if (!$ARGV[0] || !-d -r "/$ARGV[0]");

die "No (valid) destination directory path given.\n"
    if (!$ARGV[1] || !-d -w "/$ARGV[1]");

my $len = length($ARGV[0]);
my @files;
find (
    sub {
        my $fpath = $File::Find::name;
        return if !-r -f $fpath;
        push @files, [
            substr($fpath, $len),
            (stat($fpath))[7],
        ]
    }, $ARGV[0]
);

foreach (sort { $a->[1] <=> $b->[1] } @files) {
    if ($ARGV[2]) {
        print "$_->[1] $ARGV[0]/$_->[0] -> $ARGV[1]/$_->[0]\n";
    } else {
        my $dest = "$ARGV[1]/$_->[0]";
        my $dir = dirname($dest);
        mkdir $dir if !-e $dir;
        `cp -a "$ARGV[0]/$_->[0]" $dest`;
    }
} 
  • Usa questo: ./whatever.pl /src/path /dest/path

  • Gli argomenti dovrebbero essere entrambi percorsi assoluti ; ~o qualsiasi altra cosa che la shell si espande in un percorso assoluto va bene.

  • Se aggiungi un terzo argomento (qualsiasi cosa, tranne un letterale 0), invece di copiarlo, stamperà un rapporto standard su cosa farebbe, con dimensioni dei file in byte anteposte, ad es.

    4523 /src/path/file.x -> /dest/path/file.x
    12124 /src/path/file.z -> /dest/path/file.z

    Si noti che questi sono in ordine crescente per dimensione.

  • Il cpcomando sulla riga 34 è un comando letterale della shell, quindi puoi fare quello che vuoi con gli switch (ho usato solo -aper preservare tutti i tratti).

  • File::Finde File::Basenamesono entrambi i moduli principali, cioè sono disponibili in tutte le installazioni di perl.


probabilmente, questa è l'unica risposta corretta qui. O era ... il titolo - appena cambiato ...? La mia finestra del browser si chiama cp - copy smallest files first?ma il titolo del post è copy smallest files first?Ad ogni modo, le opzioni non fanno mai male è la mia filosofia, ma comunque tu e David siete gli unici che hanno usato cpe siete gli unici a avercela fatta.
mikeserv,

@mikeserv L'unico motivo che ho usato cpera perché è il modo più semplice per preservare le caratteristiche del file * nix nel perl (orientato alla piattaforma). Il motivo indicato nella barra del browser cp - è a causa di una funzione SE (IMO goofy) in base alla quale il più popolare dei tag selezionati viene visualizzato come prefisso al titolo effettivo.
Riccioli d'oro

Ok, quindi ritiro il mio complimento. Non proprio, spesso non vedi pearluscire dalla falegnameria qui intorno.
Mikeserv,

1

un'altra opzione sarebbe usare cp con l'output di du:

oldIFS=$IFS
IFS=''
for i in $(du -sk *mpg | sort -n | cut -f 2)
do
    cp $i destination
done
IFS=$oldIFS

Questo potrebbe ancora essere fatto su una riga, ma l'ho diviso in modo da poterlo leggere


Non devi almeno fare qualcosa per $ IFS?
Mikeserv,

Sì ... Continuo a supporre che nessuno abbia una nuova riga nei nomi dei file
David Wilkins,

1
Anche questo non sembra gestire la ricorsione attraverso la gerarchia di directory descritta dall'OP.
cpugeniusmv,

1
@cpugeniusmv Corretto ... In qualche modo mi è sfuggita la parte ricorsiva .... Potrei modificarla per gestire la ricorsione, ma a questo punto altre risposte fanno un lavoro migliore. Lascio questo qui nel caso in cui aiuti qualcuno che vede la domanda.
David Wilkins,

1
@DavidWilkins - questo aiuta molto.
nbubis,
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.