Никакой альтернативы не требуется, я просто:
В качестве примера:
Добавьте вам /etc/fstab
подобную строку для монтирования при загрузке файловой системы dropbox.
/home/user/.dropbox-store /home/user/Dropbox ext4 defaults,loop 0 0
Выполнить
dropbox stop
dd if=/dev/zero bs=1k count=1 seek=2097152 of=~/.dropbox-store
/sbin/mkfs.ext4 -L DropboxHome ~/.dropbox-store
mv ~/Dropbox ~/Dropbox_
mkdir ~/Dropbox
# emulate boot mount to check fstab syntax
sudo mount -a
sudo chown user.users ~/Dropbox
rsync -av ~/Dropbox_/. ~/Dropbox/.
dropbox start
Это грубое решение. Это просто дорожная карта, которую вы должны адаптировать к своим потребностям. Например. решение fstab
приемлемо только на однопользовательском ноутбуке, как у меня.
#!/usr/bin/perl
use strict;
my $prev = '';
my (@sums,@avg) = ();
my $count = 0;
while(<>) {
chomp;
if (m/^Timestamp/) {
my @headers = split /,/;
# insert "Ave_" at start of each header
@headers = map { "Ave_". $_ } @headers;
# replace Timestamp header with Date,Hour headers.
splice @headers,0,1,qw(Date Hour);
print join(",",@headers), "\n";
next;
};
my (@data) = split /,/;
# extract and remove date and hour from first element of @data
(my $current = shift @data) =~ s/^(.*) (\d\d):.*$/$1,$2/;
if ($count == 0 || $current eq $prev) {
# add each field in @data to the same field in @sums
foreach my $i (0..$#data) { $sums[$i] += $data[$i] };
$prev = $current;
$count++;
next unless eof;
};
# calculate and print the averages for the previous hour
foreach my $i (0..$#sums) { $avg[$i] = $sums[$i] / $count };
print join(",", $prev, @avg), "\n";
# special case handling for when there's a new date/hour on the
# last line of file (otherwise it wouldn't get printed)
if (eof && $prev ne $current) {
print join(",", $current, @data), "\n";
};
@sums = @data;
@avg = ();
$prev = $current;
$count = 1;
};
Это должно работать с любым количеством полей данных.
Сохранить как, например, average.pl
, сделать его исполняемым с помощью chmod +x average.pl
и запустить как:
$./average.pl input.csv
Date,Hour,Ave_data1,Ave_data2
2018 07 16,13,24.8,453
2018 07 16,14,18,457
2018 07 16,15,234,459
2018 07 16,17,23,845
2018 07 16,18,239,453
2018 07 17,10,29,452
2018 07 18,13,49,451
2018 07 19,13,28,456
map
, циклах и итераторах:К вашему сведению, циклы foreach my $i...
могут быть переписаны -для использования функции Perl map
вместо (см. perldoc -f map
, но, короче говоря,:map
перебирает список, делая что-то с каждым элементом, и возвращает либо новый сгенерированный список, либо количество элементов в этом сгенерированном списке ). Это более идиоматично perl, но, вероятно, его сложнее понять начинающим программистам perl. например.
foreach my $i (0..$#data) { $sums[$i] += $data[$i] };
could be written as:
@sums = map { $sums[$_] + $data[$_] } 0..$#data;
Оба они перебирают индексы массива @data(0..$#data
). Цикл for создает/изменяет элементы @sums напрямую, в то время как map
возвращает новый массив сумм, который затем присваивается массиву @sums.
Вместо использования $i
в качестве переменной-итератора функция map
автоматически создает и использует (локализованную )скалярную переменную с именем $_
. $_
используется везде в perl и является неявным аргументом (, т. е. аргументом )по умолчанию для большинства функций, когда аргумент не указан. например. print
без аргумента на самом деле print $_
, а split /,/
на самом деле split /,/, $_
. Это также неявно для операторов сопоставления с образцом, например. s/foo/bar
на самом деле $_ =~ s/foo/bar/
.
Аналогично, while (<>)
на самом деле что-то вродеwhile (defined($_ = <>))
(т.е. прочитать строку из входного файла или stdin, и если там было что читать, присвоить $_и оценить как истину. В противном случае оцените как false и завершите цикл while
).
$_
часто неофициально называют «текущей вещью» или «вещью». См. man perlvar
и найдите \$_
для более подробной информации. Существует также эквивалент массива @_
, который используется для параметров, передаваемых подпрограмме.
foreach my $i (0..$#sums) { $avg[$i] = $sums[$i] / $count };
could be written as:
@avg = map { $_ / $count } @sums;
Здесь цикл foreach
перебирает индексы @sums (0..$#sums
),в то время как map
перебирает значения массива @sums
. Опять же, цикл foreach
изменяет каждый элемент массива @avg
напрямую, в то время как map
возвращает новый массив, который назначается @avg
.
Обе формы производят идентичный вывод в этом скрипте, и обе формы полезны, но Perl-программисты со временем склоняются к использованию map
, потому что это универсальный полезный инструмент для перебора списков любого типа. И короче для ввода, чем цикл for/foreach, который делает то же самое. И потому, что через некоторое время становится естественным думать о своих данных в терминах списков, массивов и хэшей.
Он часто используется для преобразования массива в хэш (или значений или ключей хеша в массив ).
Кстати, map
не обязательно должен возвращать массив, блок кода в {... }
может делать все, что может делать код perl, а возвращаемое значение может быть просто отброшено или (, если оно присвоено скалярная переменная )возвращает количество любого сгенерированного списка.
напр. первый цикл foreach также может быть записан как:
map { $sums[$_] += $data[$_] } 0..$#data;
Это напрямую изменяет массив @sums (так же, как это делает цикл foreach ), и любое возвращаемое значение отбрасывается (, т. е. не присваивается какой-либо переменной ).
И, конечно же, второй цикл foreach
можно было бы записать и как:
map { $avg[$_] = $sums[$_] / $count } 0..$#sums;
А GNU awk
путь:
#!/usr/bin/awk -f
BEGIN {
FS=OFS=","
}
NR == 1 {
# Build the header here
for (i = 2; i <= NF; i++) oh = oh OFS "Ave_" $i
print "Date", "Hour" oh
next
}
{
# Split date and time and build a timestamp with it.
# Set MM and SS to 0 to aggregate data from the same hour
split($1, a, " ")
sub(/:.*/, "", a[4])
ct = mktime(a[1] " " a[2] " " a[3] " " a[4] " 00 00")
# If the 'current time' differ from the 'old time' then
# do the average and print the line
if (ct != ot && ot) {
for (i in avg){
avg_h = avg_h OFS (avg[i] / cnt[i])
delete avg[i]
delete cnt[i]
}
sub(/^,/, "", avg_h)
print cd, ch, avg_h
avg_h = ""
saved = 0
}
j = 0
for (i = 2; i <= NF; i++) {
avg[j] += $i
cnt[j++] += 1
}
# Do the assignment if and only something has changed
if (!saved) {
saved = 1
ot = ct
cd = a[1] " " a[2] " " a[3]
ch = a[4]
}
}
END {
# There are something else? Print it
for (i in avg)
avg_h = avg_h OFS (avg[i] / cnt[i])
sub(/^,/, "", avg_h)
print cd, ch, avg_h
}
Запуск от имени:./script.awk data