ฉันมีไดเรกทอรีขนาดใหญ่ที่มีไดเรกทอรีย่อยและไฟล์ที่ฉันต้องการคัดลอกซ้ำ
มีวิธีใดบ้างที่จะบอกcp
ว่าควรทำการคัดลอกตามขนาดไฟล์เพื่อให้ไฟล์ที่เล็กที่สุดถูกคัดลอกก่อน?
ฉันมีไดเรกทอรีขนาดใหญ่ที่มีไดเรกทอรีย่อยและไฟล์ที่ฉันต้องการคัดลอกซ้ำ
มีวิธีใดบ้างที่จะบอกcp
ว่าควรทำการคัดลอกตามขนาดไฟล์เพื่อให้ไฟล์ที่เล็กที่สุดถูกคัดลอกก่อน?
คำตอบ:
นี่เป็นการทำงานทั้งหมดในครั้งเดียว - ในไดเรกทอรีลูกทั้งหมดทั้งหมดในสตรีมเดียวโดยไม่มีปัญหาชื่อไฟล์ มันจะคัดลอกจากที่เล็กที่สุดไปหามากที่สุดทุกไฟล์ที่คุณมี คุณจะต้องmkdir ${DESTINATION}
ถ้ามันไม่ได้อยู่
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 -
คุณรู้อะไรไหม สิ่งนี้ไม่ได้ทำคือไดเรกทอรีย่อยที่ว่างเปล่า ฉันสามารถเปลี่ยนเส้นทางบางส่วนผ่านไปป์ไลน์นั้นได้ แต่มันเป็นเพียงสภาวะการแข่งขันที่รอให้เกิดขึ้น ง่ายที่สุดน่าจะดีที่สุด ดังนั้นให้ทำดังนี้
find . -type d -printf 'mkdir -p "'"${DESTINATION}"'/%p"\n' |
. /dev/stdin
หรือเนื่องจาก Gilles เป็นจุดที่ดีมากในคำตอบของเขาในการสงวนสิทธิ์ไดเรกทอรีฉันควรลองด้วย ฉันคิดว่ามันจะทำ:
find . -type d -printf '[ -d "'"${DESTINATION}"'/%p" ] ||
cp "%p" -t "'"${DESTINATION}"'"\n' |
. /dev/stdin
ฉันยินดีที่จะเดิมพันที่เร็วกว่าmkdir
เดิม
นี่เป็นวิธีที่รวดเร็วและสกปรกโดยใช้ rsync
นี่คือวิธีการที่รวดเร็วและสกปรกใช้สำหรับตัวอย่างนี้ฉันกำลังพิจารณาอะไรที่ต่ำกว่า 10 MB ให้เป็น "เล็ก"
เริ่มแรกถ่ายโอนไฟล์ขนาดเล็ก:
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.
แน่นอนลำดับของการถ่ายโอนไฟล์ต่อไฟล์ไม่ได้เล็กที่สุดไปหามากที่สุด แต่ฉันคิดว่ามันอาจเป็นทางออกที่ง่ายที่สุดที่ตรงกับความต้องการของคุณ
--copy-dest=DIR
และ / หรือ--compare-dest=DIR
ฉันคิดว่า ฉันรู้เพียงแค่สาเหตุที่ฉันต้องเพิ่ม--hard-dereference
ตัวเองtar
หลังจากโพสต์คำตอบของตัวเองเพราะฉันหายไปเชื่อมโยง ฉันคิดว่าrsync
จริง ๆ แล้วจะทำงานเฉพาะกับระบบไฟล์ในท้องถิ่นกับคนอื่น ๆ ต่อไป - ฉันเคยใช้มันกับปุ่ม USB และมันจะท่วมบัสถ้าฉันตั้งค่าขีด จำกัด แบนด์วิดท์ ฉันคิดว่าฉันควรใช้อย่างใดอย่างหนึ่งเหล่านั้นแทน
ไม่cp
โดยตรงนั่นเป็นสิ่งที่เกินความสามารถของมัน แต่คุณสามารถจัดการโทรcp
ในไฟล์ตามลำดับที่ถูกต้อง
Zsh ช่วยให้จัดเรียงไฟล์ตามขนาดได้อย่างสะดวกด้วยโปรแกรมขยายรอบตัว นี่คือตัวอย่าง zsh ซึ่งคัดลอกไฟล์ในการเพิ่มคำสั่งของขนาดจากใต้ภายใต้/path/to/source-directory
/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
แทนการวนซ้ำคุณสามารถใช้zcp
ฟังก์ชัน อย่างไรก็ตามคุณต้องสร้างไดเรกทอรีปลายทางก่อนซึ่งสามารถทำได้ใน cryptic 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'
สิ่งนี้ไม่รักษาความเป็นเจ้าของไดเรกทอรีต้นทาง หากคุณต้องการที่คุณจะต้องสมัครเข้าโปรแกรมคัดลอกที่เหมาะสมเช่นหรือ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
ข้อโต้แย้งที่ควรจะเป็นทั้งเป็นเส้นทางที่แน่นอน ; ~
หรือสิ่งอื่นใดที่เชลล์ขยายไปสู่พา ธ สัมบูรณ์นั้นใช้ได้
หากคุณเพิ่มอาร์กิวเมนต์ที่สาม (สิ่งใดก็ตามยกเว้นตัวอักษร0
) แทนที่จะคัดลอกมันจะพิมพ์รายงานมาตรฐานของสิ่งที่มันจะทำกับมาตรฐานขนาดไฟล์ในไบต์เสริมเช่น
4523 /src/path/file.x -> /dest/path/file.x
12124 /src/path/file.z -> /dest/path/file.z
สังเกตว่าสิ่งเหล่านี้เรียงตามขนาดจากน้อยไปมาก
cp
คำสั่งในบรรทัดที่ 34 เป็นคำสั่งของเชลล์ตัวอักษรเพื่อให้คุณสามารถทำสิ่งที่คุณต้องการด้วยสวิทช์ (ผมใช้เพียง-a
เพื่อรักษาลักษณะทั้งหมด)
File::Find
และFile::Basename
เป็นทั้งโมดูลหลักคือพวกมันมีอยู่ในการติดตั้ง Perl ทั้งหมด
cp - copy smallest files first?
แต่ชื่อของโพสต์นั้นเป็นเพียงแค่copy smallest files first?
ตัวเลือกที่ไม่เคยเจ็บปวดคือปรัชญาของฉัน แต่ถึงกระนั้นคุณและเดวิดก็เป็นคนเดียวที่ใช้cp
และคุณเป็นคนเดียวที่ดึงมันออกมา
cp
เพราะมันเป็นวิธีที่ง่ายที่สุดในการรักษาลักษณะไฟล์ * nix ใน perl (ข้ามแพลตฟอร์ม) เหตุผลที่แถบเบราว์เซอร์ของคุณบอกว่าcp -
เป็นเพราะคุณสมบัติ SE (IMO goofy) SE โดยที่แท็กที่ได้รับความนิยมสูงสุดจะปรากฏขึ้นนำหน้าชื่อจริง
pearl
ออกมาจากงานไม้แถวนี้บ่อยนัก
ตัวเลือกอื่นจะใช้ cp กับผลลัพธ์จาก du:
oldIFS=$IFS
IFS=''
for i in $(du -sk *mpg | sort -n | cut -f 2)
do
cp $i destination
done
IFS=$oldIFS
สิ่งนี้สามารถทำได้ในหนึ่งบรรทัด แต่ฉันแยกมันเพื่อให้คุณสามารถอ่านได้