sao chép tập tin nhỏ nhất trước?


15

Tôi có một thư mục lớn chứa các thư mục con và tệp mà tôi muốn sao chép đệ quy.

Có cách nào để nói cprằng nó nên thực hiện thao tác sao chép theo thứ tự kích thước tệp, để các tệp nhỏ nhất được sao chép trước không?


1
Chỉ để chắc chắn rằng không có vấn đề XY liên quan, bạn có thể giải thích lý do tại sao bạn muốn làm điều này?
goldilocks

4
@ TAFKA'goldilocks '- Tôi có rất nhiều tệp video và tôi muốn kiểm tra chất lượng từng thư mục. Video nhỏ nhất sẽ cho tôi một dấu hiệu nhanh về việc phần còn lại của các tệp có xấu không.
nbubis

Câu trả lời:


10

Điều này thực hiện toàn bộ công việc trong một lần - trong tất cả các thư mục con, tất cả trong một luồng mà không có bất kỳ vấn đề nào về tên tệp. Nó sẽ sao chép từ nhỏ nhất đến lớn nhất mỗi tệp bạn có. Bạn sẽ cần mkdir ${DESTINATION}nếu nó không tồn tại.

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 -

Bạn biết những gì, mặc dù? Điều này không làm là thư mục con trống . Tôi có thể thực hiện một số chuyển hướng trên đường ống đó, nhưng đó chỉ là một điều kiện cuộc đua đang chờ xảy ra. Đơn giản nhất có lẽ là tốt nhất. Vì vậy, chỉ cần làm điều này sau đó:

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

Hoặc, vì Gilles đã đưa ra một điểm rất tốt trong câu trả lời của mình để bảo vệ các quyền của thư mục, tôi cũng nên thử. Tôi nghĩ rằng điều này sẽ làm điều đó:

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

Tôi sẵn sàng đặt cược nhanh hơn mkdirdù sao đi nữa.


1
Chết tiệt bạn mikeerv! +1
goldilocks

3
@ TAFKA'goldilocks 'Tôi sẽ coi đó là một lời khen. Cảm ơn rất nhiều.
mikeerv

15

Đây là một phương pháp nhanh chóng và bẩn bằng cách sử dụng rsync. Trong ví dụ này, tôi đang xem mọi thứ dưới 10 MB là "nhỏ".

Đầu tiên chỉ chuyển các tệp nhỏ:

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

Sau đó chuyển các tập tin còn lại. Các tệp nhỏ được chuyển trước đó sẽ không được sao chép lại trừ khi chúng được sửa đổi.

rsync -a srcdir dstdir

Từ 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.

Tất nhiên, thứ tự chuyển từng tập tin không hoàn toàn nhỏ nhất đến lớn nhất, nhưng tôi nghĩ đó có thể là giải pháp đơn giản nhất đáp ứng tinh thần yêu cầu của bạn.


Tại đây, bạn nhận được 2 bản sao của liên kết cứng và liên kết mềm được chuyển đổi thành các tệp thực tế cho hai bản sao của mỗi liên kết. Bạn sẽ làm tốt hơn rất nhiều với --copy-dest=DIRvà / hoặc --compare-dest=DIRtôi nghĩ. Tôi chỉ biết nguyên nhân tôi phải tự thêm --hard-dereferencevào tarsau khi đăng câu trả lời của riêng mình vì tôi đã bỏ lỡ các liên kết. Tôi nghĩ rằng rsyncthực sự hành xử cụ thể hơn đối với các hệ thống tệp cục bộ với những hệ thống khác dù sao - tôi đã từng sử dụng nó với các phím USB và nó sẽ tràn ngập xe buýt trừ khi tôi đặt giới hạn băng thông. Tôi nghĩ rằng tôi nên sử dụng một trong những người khác thay thế.
mikeerv

1
+1 cho "phương pháp nhanh và bẩn". Đơn giản hơn thường tốt hơn ít nhất cho mục đích tự động hóa và khả năng bảo trì trong tương lai. Tôi nghĩ rằng điều này thực sự là khá sạch sẽ. "Elegant" vs "kydgy" và "mạnh mẽ" vs "không ổn định" đôi khi có thể mâu thuẫn như mục tiêu thiết kế nhưng có một sự cân bằng tốt có thể bị đánh bại, và tôi nghĩ rằng điều này là thanh lịch khá mạnh mẽ.
tự đại diện

4

Không cptrực tiếp, điều đó vượt quá khả năng của nó. Nhưng bạn có thể sắp xếp để gọi cpcác tập tin theo đúng thứ tự.

Zsh thuận tiện cho phép sắp xếp các tệp theo kích thước với vòng loại toàn cầu . Đây là đoạn mã zsh sao chép các tệp theo thứ tự tăng kích thước từ dưới /path/to/source-directorylên dưới /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

Thay vì một vòng lặp, bạn có thể sử dụng zcpchức năng. Tuy nhiên, bạn cần tạo các thư mục đích trước, có thể được thực hiện trong một oneliner khó hiểu.

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'

Điều này không bảo vệ quyền sở hữu của các thư mục nguồn. Nếu bạn muốn điều đó, bạn sẽ cần tranh thủ một chương trình sao chép phù hợp như cpiohoặc pax. Nếu bạn làm điều đó, bạn không cần phải gọi cphoặc zcpngoài ra.

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

2

Tôi không nghĩ có cách nào cp -rđể làm điều này trực tiếp. Vì đó có thể là một khoảng thời gian không xác định trước khi bạn nhận được một giải pháp find/ thuật sĩ awk, đây là tập lệnh perl nhanh:

#!/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`;
    }
} 
  • Dùng cái này: ./whatever.pl /src/path /dest/path

  • Các đối số nên là cả hai đường dẫn tuyệt đối ; ~hoặc bất cứ điều gì khác mà vỏ mở rộng đến một đường dẫn tuyệt đối là tốt.

  • Nếu bạn thêm một đối số thứ ba (bất cứ điều gì, ngoại trừ bằng chữ 0), thay vì sao chép, nó sẽ in ra để báo cáo chuẩn về những gì nó sẽ làm, với kích thước tệp được tính theo byte, ví dụ:

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

    Lưu ý rằng đây là theo thứ tự tăng dần theo kích thước.

  • Các cplệnh trên dòng 34 là một lệnh shell đen, vì vậy bạn có thể làm bất cứ điều gì bạn muốn với thiết bị chuyển mạch (Tôi chỉ được sử dụng -ađể bảo vệ tất cả những đặc điểm).

  • File::FindFile::Basenamelà cả hai mô-đun cốt lõi, tức là chúng có sẵn trong tất cả các cài đặt của perl.


có thể nói, đây là câu trả lời đúng duy nhất ở đây Hay đó là ... tiêu đề - vừa mới thay đổi ...? Cửa sổ trình duyệt của tôi được gọi cp - copy smallest files first?nhưng tiêu đề của bài viết chỉ là copy smallest files first?Dù sao, các tùy chọn không bao giờ bị tổn thương là triết lý của tôi, nhưng vẫn vậy, bạn và David là những người duy nhất đã sử dụng cpvà bạn là người duy nhất rút nó ra.
mikeerv

@mikeerv Lý do duy nhất tôi sử dụng cplà vì đó là cách đơn giản nhất để bảo tồn các đặc điểm tệp * nix trong perl (định hướng đa nền tảng). Lý do thanh trình duyệt của bạn nói cp - là vì tính năng SE (ngu ngốc IMO), theo đó, các thẻ phổ biến nhất được chọn xuất hiện trước tiêu đề thực tế.
goldilocks

Ok, sau đó tôi rút lại lời khen của mình. Không thực sự, bạn thường không thấy pearlra khỏi đồ gỗ quanh đây.
mikeerv

1

một tùy chọn khác là sử dụng cp với đầu ra từ du:

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

Điều này vẫn có thể được thực hiện trên một dòng, nhưng tôi chia nó để bạn có thể đọc nó


Bạn ít nhất cần phải làm gì đó về $ IFS?
mikeerv

Có ... Tôi tiếp tục cho rằng không ai có dòng mới trong tên tệp của họ
David Wilkins

1
Điều này dường như cũng không xử lý đệ quy thông qua hệ thống phân cấp thư mục mà OP mô tả.
cpugeniusmv

1
@cpugeniusmv Đúng ... Tôi bằng cách nào đó đã bỏ qua phần đệ quy .... Tôi có thể sửa đổi phần này để xử lý đệ quy, nhưng tôi nghĩ tại thời điểm này các câu trả lời khác làm tốt hơn. Tôi sẽ để nó ở đây trong trường hợp nó giúp ai đó nhìn thấy câu hỏi.
David Wilkins

1
@DavidWilkins - điều này giúp ích rất nhiều.
nbubis
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.