улучшить генерацию шуточных данных

Есть два потока вывода от процесса, которые обычно отправляются на терминал: стандартный вывод (который привязан к файловому дескриптору 1) и стандартный поток ошибок. (который привязан к файловому дескриптору 2). Это позволяет легко разделить ожидаемый результат и сообщения об ошибках или другую диагностическую информацию, которая обычно не ожидается.

Когда вы используете перенаправление (> ), оно запускает только стандартный вывод в указанный файл или место, оставляя стандартную ошибку нетронутой. Это позволяет вам увидеть, есть ли ошибки (например, те, которые вы видите).

Если вы хотите отправить весь вывод, включая стандартную ошибку, вам необходимо перенаправить оба потока:

/path/to/program arg1 arg2 > /dev/null 2>&1

Или, альтернативно, но более явно:

/path/to/program arg1 arg2 > /dev/null 2> /dev/null

Синтаксис 2> & 1 означает «Отправить вывод в настоящее время идет к файловому дескриптору 2 в то же место, куда идет вывод, идущий в файловый дескриптор 1 ".> опускает значение по умолчанию для FD1, поэтому семантически оно совпадает с 1> , что может сделать 2> более понятным.

3
27.08.2017, 16:22
2 ответа

Конечный результат см. в самом конце.

for i in {1..1000000..1}
do 
  echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" >> $F
done;

Циклы оболочки медленные, и есть две основные вещи, которые делают этот конкретный цикл особенно медленным:

  1. Открытие и добавление к файлу на каждой итерации.
  2. Два выполнения внешних утилит ( shufи date) на каждой итерации. эхо, скорее всего, встроено в оболочку, так что возникает меньше накладных расходов.

Перенаправление вывода легче всего исправить:

for i in {1..1000000..1}
do 
  echo "$i,$(date -d "2017-08-01 + $(shuf -i 1-31 -n 1) days" +'%Y-%m-%d')" 
done >"$F"

Это открывает выходной файл только один раз и держит его открытым на протяжении всего цикла.


Остальной код можно сделать более эффективно с помощью awkи GNU date(поскольку вы используете shuf, я полагаю, что вы находитесь на Linux, что означает, что вполне вероятно, что датана самом деле является GNU дата).

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null

Эта штука генерирует 100 строк типа

2017-08-01 + 22 days
2017-08-01 + 31 days
2017-08-01 + 11 days
2017-08-01 + 27 days
2017-08-01 + 27 days
2017-08-01 + 20 days
(etc.)

Давайте загрузим их в GNU date. GNU dateимеет этот флаг, -f, который позволяет нам пакетно задавать несколько спецификаций даты, например те, которые выводятся нашей программой awk:

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d'

Теперь мы получаем

2017-08-23
2017-08-27
2017-08-21
2017-08-29
2017-08-25
2017-08-17
2017-08-07
(etc.)

. Теперь нужно просто добавить уникальный идентификатор (целое число) к каждой строке:

awk 'END { for (i=0;i<100;++i) { printf("2017-08-01 + %d days\n", 1+int(31*rand())) }}' /dev/null |
date -f - +'%Y-%m-%d' |
awk -vOFS=',' '{ print NR, $0 }'

Это дает вам

1,2017-08-06
2,2017-08-17
3,2017-08-25
4,2017-08-28
5,2017-08-14
6,2017-08-15
7,2017-08-17
8,2017-08-10
9,2017-08-16
10,2017-08-08
(etc.)

И вот мы закончили. И в процессе я совершенно забыл, что у нас есть петля оболочки. Оказывается не нужен.

Просто увеличьте 100до любого значения и настройте генератор случайных чисел в соответствии со своими потребностями. rand()возвращает значение с плавающей запятой, такое что 0 <= число < 1.


Очевидно, что если вам просто нужны случайные даты в августе (месяц с 31 днем), вы можете вообще обойти дату:

awk 'END { for (i=1;i<=100;++i) { printf("%d,2017-08-%02d\n", i, 1+int(31*rand())) }}' /dev/null

С помощью GNU awkи Майка awk( mawk), но не с BSD awk, вы даже можете выполнить правильную обработку даты непосредственно в awk:

awk 'END { for (i=1;i<=100;++i) { printf("%d,%s\n", i, strftime("%Y-%m-%d", 1501545600 + int(2678400*rand()),1 )) }}' /dev/null

Теперь мы тем не менее, работа с временными метками Unix, а не с днями. 1501545600 соответствует «Вторник, 1 августа, 00:00:00 UTC 2017», а в 31 дне 2678400 секунд.

3
27.01.2020, 21:18
# A "random" date between 2000-01-01 and 2025-12-28
# Only uses day 01 to 28 
rand_date() {
    printf "%4d-%02d-%02d" $((RANDOM%25+2000)) $((RANDOM%12+1)) $((RANDOM%28+1))
}

csv_data() {
    for ((i=1; i<="$1"; i++)); do printf "%d,%s\n" $i $(rand_date); done
}
$ time (csv_data 1000000 > data.csv)
real    7m26.683s
user    0m36.376s
sys 1m57.768s

Perl, наверное, быстрее, давайте попробуем

$ cat data.pl
#!/usr/bin/perl
$, = ",";
$\ = "\n";

sub rand_date {
    sprintf "%4d-%02d-%02d", int(rand(25))+2000, int(rand(12))+1, int(rand(28))+1;
}

sub csv_data {
    my $n = shift;
    for ($i = 1; $i <= $n; $i++) {
        print $i, rand_date();
    }
}

csv_data(1_000_000);
$ time (perl data.pl > data.csv)

real    0m0.881s
user    0m0.876s
sys 0m0.004s

Хм, да, чуть быстрее...

1
27.01.2020, 21:18

Теги

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