Как разделить список имени файла в наборах на 5 ГБ?

Вместо того, чтобы смешать с системным уровнем Python, мог бы я предлагать использовать что-то как virtualenv вместе с virtualenvwrapper. Вместе эти 2 инструмента делают это довольно тривиальным для выдерживания собственных локальных копий библиотек Python + вместо того, чтобы иметь необходимость попытаться сохранить системную установку уровня Python в состоянии OK для системного программного обеспечения уровня, которое требует его.

virtualenv

virtualenv является инструментом для создания изолированных сред Python.

Это создает среду, которая имеет ее собственные каталоги установки, который не совместно использует библиотеки с другими virtualenv средами (и дополнительно не получает доступ к глобально установленным библиотекам ни один).

virtualenvwrapper

virtualenvwrapper является рядом расширений virtualenv инструмента Ian Bicking. Расширения включают обертки для создания и удаления виртуальных сред и в других отношениях руководящий Ваш рабочий процесс разработки, помогая работать над больше чем одним проектом за один раз, не представляя конфликты в их зависимостях.

Большинство языков обеспечивает эти типы инструментов теперь. См. мое сообщение по этому названному вопросу: Рекомендуемый дистрибутив Linux для статистики? для других языков также.

4
03.04.2014, 05:06
7 ответов

Похоже, вы просите решение проблемы упаковки в контейнеры . Насколько нам известно, не существует способа сделать это одновременно оптимально и быстро (но мы также не знаем, что его нет; это открытый вопрос).

Но вы можете приблизиться. Одна утилита для этого называется datapacker . Не использовал тот; нашел его поиском. Я уверен, что есть еще кое-что.

Лично я использую тот, который написал сам. (Обратите внимание, что все они выводят список файлов, которые попадают в каждую корзину; вы можете легко использовать cat , чтобы собрать их в один текстовый файл.) Комментарий относится к bfpp , который это еще одна программа, которую я написал, которая находит оптимальное решение, если у вас есть только несколько (например, менее 10) файлов:

#!/usr/bin/perl -w
#
# Sane bin packer. Attempts to finds a way to arrange a set of items
# into the fewest bins. Uses first-fit decreasing, which will get within
# 11⁄9⋅OPT + 6⁄9 (where OPT is the optimal number of discs, as found by
# bfpp). Unlike bfpp, this program will finish quickly.
#
# Also, this program features many more non-ASCII characters than bfpp.
# This is probably its most important feature.

use strict;
use 5.010;
use Data::Dump qw(pp);
use File::Next;
use List::Util qw(sum);
use String::ShellQuote qw(shell_quote);
use Carp qw(confess croak carp);

sub usage($) {
    say STDERR "Usage: $0 bin-size-megabytes file1 file2 ...";
    exit shift;
}

sub parse_command_line() {
    usage(1) if @ARGV < 3;
    usage(0) if $ARGV[0] =~ /^--?[h?]/;

    my $size = shift @ARGV;

    given ($size) {
        when (/^dvd5?$/) {
            $size = 4011;
        }
        when (/^dvd9$/) {
            $size = 7291;
        }
        when (/^bd(-r)?1?$/) {
            $size = 21360;
        }
        when (/^bd(-r)?2$/) {
            $size = 42720;
        }
        when (/^\d+$/) {
            # do nothing, already number
        }
        default {
            say STDERR "ERROR: Size must be number or known size constant (dvd, dvd9, bd, bd2)";
            usage(1);
        }
    }

    return {
        bin_size => $size * 1024 * 1024,
        items    => get_item_info(@ARGV),
    };
}

sub get_item_info {
    my %items;
    my ($in_group, $group_no) = (0, 0);
    foreach my $item (@_) {
        if ('(' eq $item ) {
            $in_group and confess "Nested groups not supported";
            $in_group = 1;
            ++$group_no;
            next;
        } elsif (')' eq $item) {
            $in_group or croak "Can't close a group when none open";
            $in_group = 0;
            next;
        }

        my $key;
        $in_group and $key = "!!__GROUP${group_no}__!!";

        if (-f $item) {
            defined $key or ($key = $item) =~ s/\..{2,4}$//;
            push @{$items{$key}{members}}, $item;
            $items{$key}{size} += -s _;
        } elsif (-d $item) {
            $key //= $item;
            my $files = File::Next::files($item);
            while (defined(my $file = $files->())) {
                push @{$items{$key}{members}}, $file;
                $items{$key}{size} += -s $file;
            }
        } else {
            confess "Not sure how to handle $item (weird type or doesn't exist)"
        }
    }

    $in_group and carp "WARNING: Group wasn't closed";

    return \%items;
}

sub check_sanity($) {
    my $info = shift;
    my $_;

    my $binsize = $info->{bin_size};
    my @dontfit = grep $info->{items}{$_}{size} > $binsize,
        keys %{$info->{items}};

    if (@dontfit) {
        say "\nWARNING! WARNING! WARNING! WARNING!";
        say "The following items are larger than the bin size:";
        say pp(\@dontfit);
        say "This is going to lead to some funny results.\n";
        say "WARNING! WARNING! WARNING! WARNING!\n";
    }

    return $info;
}

sub approximate_best {
    my $info = shift;

    my @sorted
        = sort { $info->{items}{$::b}{size} <=> $info->{items}{$::a}{size} }
        keys %{$info->{items}};

    my @bins;

FILE: foreach my $file (@sorted) {
        my $size = $info->{items}{$file}{size};
    BIN: foreach my $bin (@bins) {
            next BIN unless $bin->{remaining} >= $size;
            push @{$bin->{contents}}, $file;
            $bin->{remaining} -= $size;
            next FILE;
        }
        # didn't find a bin, open a new one
        push @bins,
            {
            remaining => $info->{bin_size} - $size,
            contents  => [$file],
            };
    }

    $info->{bins} = \@bins;

    return $info;
}

sub print_bins($) {
    my $info = shift;
    my $_;

    my $bins = $info->{bins};

    #<<< [Hide this mess from PerlTidy]
    use locale;
    my @bins_full = map { # for each disk
        [ sort( # sort each disk's fileset
            map { # for each fileset
                @{$info->{items}{$_}{members}}
            } @{$_->{contents}}
        ) ];
    } @$bins;
    #>>>
    for (my $d = 0; $d < @bins_full; ++$d) {
        print <<DISC
DISC #@{[$d + 1]}:   (@{[ int($bins->[$d]{remaining}/1024/1024) ]} MiB empty)
   @{[ join(qq{\n   }, @{$bins_full[$d]}) ]}

DISC
    }

    say "As space-separated, escaped values (for shell):";
    for (my $d = 0; $d < @bins_full; ++$d) {
        say $d+1, q{: }, shell_quote @{$bins_full[$d]};
    }

    return undef;
}

# believe it or not, the below is code.
print_bins approximate_best check_sanity parse_command_line;
7
27.01.2020, 20:45
[1127959] Похоже, что ваш список отсортирован по размеру, или может быть легко отсортирован по размеру, так что если вы хотите немного оптимизировать это, используя

First Fit Decreasing[1128860] strategy[1128395], вы можете использовать следующий скрипт Awk (красивый и понятный) с помощью [1128396]tac[1128397] для чтения входного файла снизу (это требуется только потому, что он отсортирован по размеру файла в порядке возрастания): Затем просто объедините его с [1128398]так[1128399] в [1128400]bash[1128401]:

Например, установив [1128402]Max_bin_size = 5 * 2^20[1128403] (5 Мб), вы увидите, что некоторое дополнительное пространство потребляется меньшими по размеру файлами, которые не расположены в последовательном порядке:

 Proto  Local Address          Foreign Address        State
  TCP    10.0.0.2:56702         ARTEMIS:ms-wbt-server  ESTABLISHED
  TCP    127.0.0.1:19872        Athena:49172           ESTABLISHED
  TCP    192.168.1.127:2869     192.168.1.254:49565    TIME_WAIT
  TCP    192.168.1.127:56523    stackoverflow:http     ESTABLISHED

Компромисс - это время обработки, однако, где-то между [1128404]O(N)[1128405] и [1128406]O(N[1128861]2[1128862])[1128407] в зависимости от распределения элементов, размеров элементов и размера бина. Это достаточно быстро для Вашего примера:

0
27.01.2020, 20:45

Да. Использование сценария bash:

#!/bin/bash
groupsize=0
groupcnt=0
cat unix_StackExchange_question.txt | while read fsize fname
do
        [ "$groupsize" == "0" ] && echo "Group : GROUP_$groupcnt"
        echo -en "\t $fname\n"
        ((groupsize+=$fsize))
        #cp $fname GROUP_$groupcnt
        if [ "$groupsize" -gt "5000000" ]
        then
            ((groupcnt++))
            groupsize=0
        fi
done
5
27.01.2020, 20:45
awk -v sizelimit=5000000 -v outputfilename=shorter_list \
'BEGIN {target=outputfilename ".0000"}; '\
'{sum+=$1; '\
'if(sum>sizelimit) { file_index++; target=outputfilename "." sprintf("%04d",file_index); sum=$1;}; '\
'print $0 >target}' file

должен сделать то, что вы хотите. Тем не менее, вы должны изменить лимит размера. Я использовал меньшее значение для тестирования (что может быть полезно и для вашего тестирования).

3
27.01.2020, 20:45

Этот должен работать, но он медленный (1 мин. 18 сек. На 500 записей)

#!/bin/bash

#reformatting the initial file to remove tab

SRC=`cat file.txt | expand`

outputs_dir="outputs"

if [ ! -d "$outputs_dir" ];then
  mkdir "$outputs_dir"
else
 echo "$outputs_dir exist"
 #rm "$outputs_dir"/*
fi

#init file outputs array with 2 files first one is in case files is bigger than 5GB

foutputs_array=( "$outputs_dir"/leftover.txt "$outputs_dir"/file1.txt )

#init file size array. Each time a file will be added to an output file, its size will be added here. 
# If its size doesn't fit in an existing file, it will be added to a new file. New file will be added to foutputs_array,...
# If it doesn't fit anywhere, it will go to leftover.

fsize_array=( "0" "0" )

#5GB limit

FLIMIT=5242880

FLAG=0
i=1
array_index=1

fitIn(){

local file_size=$1
local total_size=$2
#echo "summing.." >&2
sum=$(expr $file_size + $total_size)
#echo "sum=" "$sum" >&2
if [[ "$sum" -le "$FLIMIT" ]];then
 echo 0
else
 echo 1
fi
}


while read fsize fname

do
# echo "array_index=" $array_index ${fsize_array[@]} "fsize"$fsize ${fsize_array[$array_index]} 
 check_size=`fitIn $fsize ${fsize_array[$array_index]}`
# echo "check_size" $check_size
 if [ "$fsize" -le "$FLIMIT" -a "$check_size" -eq "0" ];then
 #  echo "In limit"
   FLAG=0  
 elif [ $fsize -le $FLIMIT ];then
 #  echo "In max limit"
   FLAG=0
   while [ $check_size -eq "1" ]
    do
#     echo $array_index $i
     (( array_index++ )) 
     (( i++ )) 
     if [ -z ${fsize_array[$array_index]} ];then 
      fsize_array[$array_index]=0
     fi
     check_size=`fitIn $fsize ${fsize_array[$array_index]}`
    done
#    echo "new position" $array_index
    foutputs_array[$array_index]="$outputs_dir"/file${i}.txt
 else
  echo "$fsize $fname doesn't fit anywhere!"
  FLAG=1
  array_index=0 
 fi 

 if [ $FLAG -eq 0 ];then
  (( fsize_array[$array_index]+=$fsize ))
 fi
 echo "$fsize" "$fname" >> "${foutputs_array[$array_index]}"
 array_index=1
 i=1  
done <<< "$SRC"
0
27.01.2020, 20:45

На прекрасном, понятном Python:

#!/usr/bin/env python
with open('unix_StackExchange_question.txt') as f:
    files = f.read()
files = files.split('\n')

group = list()
group_size = 0
n = 0
for f in files:
    pair = f.split()
    if not pair:
        break
    size = int(pair[0])
    name = pair[1]
    group_size += size
    group.append(f)
    # assume the files are in ascending order as per the op's question
    if group_size + size >= 5000000:
        n += 1
        group_size = 0
        print 'file %s:\n\t' % n +'\n\t'.join(x for x in group) 
        group = list()

Просто сохраните как 5GBfiles.py и chmod + x 5GBfiles.py и готово!

0
27.01.2020, 20:45

Это должно работать с bash:

#!/bin/bash

accumulated_size=0
file_counter=0
out_file=file_0000

while read size name; do
  if [ "$accumulated_size" -gt 5368709120 ]; then
    (( file_counter += 1 ))
    out_file=$(printf 'file_%04d' "$file_counter")
    echo -n >"out_file"
    accumulated_size=0
  fi

  echo "$size $name" >>"$out_file"
  (( accumulated_size += size ))
done <unix_StackExchange_question.txt

Новый файл будет запускаться для первой строки, размер которой превышает 5GiB. Таким образом, каждый файл будет содержать размеры, суммарно превышающие 5GiB (если только последний файл не очень большой).

Обновление

Это действительно должно быть сделано с помощью awk скрипта - следующий эквивалент в awk:

awk -v out_file=file_0000 '{
    if(accumulated_size > 5368709120){
      file_counter += 1
      out_file=sprintf("file_%04d", file_counter)
      accumulated_size=0
    }

    print >out_file
    accumulated_size+=$1
  }' unix_StackExchange_question.txt
3
27.01.2020, 20:45

Теги

Похожие вопросы