web-dev-qa-db-ja.com

最初に最小のファイルをコピーしますか?

再帰的にコピーしたいサブディレクトリとファイルを含む大きなディレクトリがあります。

cpに、ファイルサイズの順にコピー操作を実行して、最小のファイルが最初にコピーされるようにする方法はありますか?

15
nbubis

これは、rsyncを使用した手っ取り早い方法です。この例では、10MB未満のものはすべて「小さい」と考えています。

最初に小さなファイルだけを転送します:

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

次に、残りのファイルを転送します。以前に転送された小さなファイルは、変更されない限り再コピーされません。

rsync -a srcdir dstdir

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  doesn’t
          affect  the  data  that  goes  into  the file-lists, and thus it
          doesn’t 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.

もちろん、ファイルごとの転送の順序は厳密には最小から最大ではありませんが、要件の精神を満たす最も簡単なソリューションかもしれないと思います。

15
cpugeniusmv

直接cpではなく、それはその能力をはるかに超えています。しかし、正しい順序でファイルに対してcpを呼び出すように手配できます。

Zshでは glob qualifier を使用してファイルをサイズ順に並べ替えることができます。これは、/path/to/source-directoryの下から/path/to/destination-directoryの下にサイズの昇順でファイルをコピーするzshスニペットです。

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

ループの代わりに、 zcp 関数を使用できます。ただし、最初に宛先ディレクトリを作成する必要があります。これは、不可解なワンライナーで実行できます。

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'

これは、ソースディレクトリの所有権を保持しません。それが必要な場合は、cpiopaxなどの適切なコピープログラムを利用する必要があります。これを行う場合、cpまたはzcpをさらに呼び出す必要はありません。

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

cp -rから直接取得する方法はないと思います。魔法のようにfind/awkソリューションを取得するまでには不確定な期間がある可能性があるため、ここに簡単な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`;
    }
} 
  • これを使用してください:./whatever.pl /src/path /dest/path

  • 引数は両方とも絶対パス;である必要があります。 ~、またはシェルが絶対パスに展開するその他のものは問題ありません。

  • 3番目の引数(リテラル0を除くすべて)を追加すると、コピーする代わりに、何が行われるかというレポートが標準出力に出力され、バイト単位のファイルサイズが付加されます。

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

    これらはサイズの昇順であることに注意してください。

  • 34行目のcpコマンドは文字通りのシェルコマンドであるため、スイッチを使用して任意の操作を実行できます(すべての特性を保持するために-aを使用しました)。

  • File::FindFile::Basenameはどちらもコアモジュールです。つまり、Perlのすべてのインストールで使用できます。

2
goldilocks

別のオプションは、duからの出力でcpを使用することです。

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

これはまだ1行で実行できますが、読みやすいように分割します

1
David Wilkins