再帰的にコピーしたいサブディレクトリとファイルを含む大きなディレクトリがあります。
cp
に、ファイルサイズの順にコピー操作を実行して、最小のファイルが最初にコピーされるようにする方法はありますか?
これは、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.
もちろん、ファイルごとの転送の順序は厳密には最小から最大ではありませんが、要件の精神を満たす最も簡単なソリューションかもしれないと思います。
直接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'
これは、ソースディレクトリの所有権を保持しません。それが必要な場合は、cpio
やpax
などの適切なコピープログラムを利用する必要があります。これを行う場合、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::Find
とFile::Basename
はどちらもコアモジュールです。つまり、Perlのすべてのインストールで使用できます。
別のオプションは、duからの出力でcpを使用することです。
oldIFS=$IFS
IFS=''
for i in $(du -sk *mpg | sort -n | cut -f 2)
do
cp $i destination
done
IFS=$oldIFS
これはまだ1行で実行できますが、読みやすいように分割します