Как можно сгенерировать последовательность случайных дат с заданным интервалом в год?

Исправлены ошибки efibootmgr путем монтирования переменных загрузки дляefibootmgr:

# mount -t efivarfs efivarfs /sys/firmware/efi/efivars

А потом efibootmgrвыдал мне ошибки про космос:

Could not prepare Boot variable: No space left on device

Исправлено путем удаления файлов дампа:

# rm /sys/firmware/efi/efivars/dump-*

А дальше побежал обычный

update-grub 
grub-install -v --target=x86_64-efi --recheck /dev/sda

и все заработало успешно!

12
18.10.2019, 18:42
5 ответов

Вы можете превратить задачу в генерацию случайного числа между числом, представляющим первую возможную дату, и числом, представляющим последнюю возможную дату (, на самом деле сразу после последней возможной )в формате эпохи unix. Все остальное обрабатывается стандартными преобразованиями даты. gawkимеет лучшее разрешение случайных чисел, чемbash(float по сравнению с 15-битным целым числом ), поэтому я буду использовать gawk. Обратите внимание, что результат rand()N — это число с плавающей запятой, такое что 0 <= N < 1, поэтому верхний предел увеличивается ниже, это предел, который нельзя перевернуть. Есть необязательный третий параметр для количества результатов.

#!/usr/bin/gawk -f
BEGIN {
    first=mktime(ARGV[1] " 01 01 00 00 00")
    last=mktime(ARGV[2]+1 " 01 01 00 00 00")
    if (ARGC == 4) { num=ARGV[3] } else { num=1 }
    ARGC=1
    range=last-first
    srand(sprintf("%d%06d", systime(), PROCINFO["pid"]))
    for (i=1; i <= num; i++) {
        print strftime("%d/%m/%Y", range*rand()+first)
    }
}   

Например:

./randomdate.gawk 1987 2017 6
26/04/1992
28/04/2010
21/04/2005
17/02/2010
06/10/2016
04/04/1998
10
27.01.2020, 19:54

Если разница в количестве лет меньше примерно 90, вы можете использовать переменную $RANDOMв bash, чтобы получить смещение в количестве дней, и использовать ограниченные возможности команды dateдля выполнения вычислений..

#!/bin/bash
s=$(date +%s -d "1/1/$1")          # start in seconds since 1 Jan 1970
e=$(date +%s -d "1/1/$(($2+1))")   # start of end year +1 in seconds
days=$(((e-s)/(24*3600)))          # number of days from start to end
factor=$((32767/$days))            # RANDOM is 0 to 32767. See how many
toobig=$(($factor*$days))          # exact multiples of days.
                                   # if RANDOM is too large, draw again
for((i=0;i<${3:-1};i++))           # produce $3 random dates
do
    r=$RANDOM                      # find a random number < toobig
    while (( r >= toobig ))        # if toobig, then loop.
    do r=$RANDOM
    done
    offset=$(($r/$factor))         # get (0,days) from (0,factor*days)
    # output horrible day/month/year for N days past start date
    date -d "$offset days 1/1/$1" +%d/%m/%Y
done

Внутренний цикл для выбора случайного числа пытается исправить смещение. Если источник случайных чисел может дать вам 0, 1, 2, 3, 4, 5, 6, 7, 8 или 9 в равной степени, и вы хотите получить случайное число от 0 до 3 включительно, то если вы получите 0 или 1 из источника вы сообщаете 0, если вы получаете 2 или 3, то вы сообщаете 1, если вы получаете 4 или 5, вы сообщаете 2, если вы получаете 6 или 7, вы сообщаете 3, и если вы получаете 8 или 9, вы игнорируете это число из источника.

5
27.01.2020, 19:54

Вот один способ, в основном в awk:

#!/bin/sh

[ "$end" -ge "$start" ] || exit 1

awk -v start=$1 -v end=$2 '
  BEGIN {
    srand();
    for(i=1; i <= 6; i++)
      printf "%02d/%02d/%d\n", 1 + rand() * 28, 1 + rand() * 12, start + rand() * (end-start);
  }
' < /dev/null

Сценарий оболочки принимает два параметра и передает их как переменные в awk, который не читает ввод и выполняет всю работу в блоке BEGIN.

После заполнения генератор случайных чисел выполняет 6 циклов по оператору printf. Этот оператор печати выбирает подмножество возможных дат в диапазоне, генерируя случайное число от 1 до 28 для дня (, безопасного для февраля ), от 1 до 12 для месяца,и между заданными начальным и конечным годами. Это случайное, но не полное покрытие --, оно никогда не напечатает дни 29 -31 для месяцев, в которых они есть.

Другой способ: использование даты GNU и функций bash:

#!/bin/bash

start=$1
end=$2

[[ "$start" -le "$end" ]] || exit 1

startsec=$(date -d "1/1/$start" +%s)
for((i=1; i<=6; i++))
do
  r=$((RANDOM % (1 + end - start)*365*24*60*60))
  date -d @$((startsec + r)) +%d/%m/%Y
done

Он работает, вычисляя секунды -с -эпохи -1 января в дату начала, затем для каждого из циклов он добавляет случайное количество секунд смещения; случайное число ограничено количеством секунд, охватывающих заданный диапазон. Дата GNU затем манипулирует этой датой в желаемом формате.

9
27.01.2020, 19:54

С date, shufиxargs:

Преобразование даты начала и окончания в «секунды с 1970 года -01 -01 00 :00 :00 UTC» и использование shufдля вывода шести случайных значений в этом диапазоне. Передайте этот результат в xargsи преобразуйте значения в нужный формат даты.

Редактировать:Если вы хотите, чтобы даты 2017 года были включены в выходные данные, вы должны добавить один год -1 с(2017-12-31 23:59:59)к дате окончания. shuf -iгенерирует случайные числа, включая начало и конец.

shuf -n6 -i$(date -d '1987-01-01' '+%s')-$(date -d '2017-01-01' '+%s')\
 | xargs -I{} date -d '@{}' '+%d/%m/%Y'

Пример вывода:

07/12/1988
22/04/2012
24/09/2012
27/08/2000
19/01/2008
21/10/1994
21
27.01.2020, 19:54

Перл:

perl -MTime::Piece -sE '
    my $t1 = Time::Piece->strptime("$y1-01-01 00:00:00", "%Y-%m-%d %H:%M:%S");
    my $t2 = Time::Piece->strptime("$y2-12-31 23:59:59", "%Y-%m-%d %H:%M:%S");
    do {
        my $time = $t1 + int(rand() * ($t2 - $t1));
        say $time->dmy;
    } for 1..6;
' -- -y1=1987 -y2=2017

Пример вывода:

10-01-1989
30-04-1995
10-12-1998
02-01-2016
04-06-2006
11-04-1987
10
27.01.2020, 19:54

Теги

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