Справочник по CLI
Берёт .tdc-конфиг, генерирует данные и записывает результат в файл или stdout — без единой строчки кода.
tdcv2 <input.tdc> [options]
tdcv2npm install -D tdcv2, pip install tdcv2 и cargo install tdcv2 кладут команду
tdcv2 в PATH из того же пакета, что несёт библиотеку. В Maven и NuGet аналога npm-ного
bin нет, поэтому у Java и C# командная строка — отдельный артефакт; вкладка для каждого
языка есть на странице Установка. Алиас делает все
команды на этой странице одинаковыми. Всё, что ниже, работает одинаково в любой
реализации.
Кроме генерации у CLI есть команды tdcv2 init и tdcv2 pack для настройки и данных —
см. Установку пакетов данных — а также
tdcv2 check (ниже) и tdcv2 format (ниже).
Опции
| Опция | Что делает |
|---|---|
-o, --output <path> | Пишет результат в файл. Без этой опции выводит в stdout. Путь, заканчивающийся на .parquet, включает писателя Parquet — другого способа получить его нет |
--seed <seed> | Переопределяет seed из <env> |
--count <n> | Переопределяет count из <env> — неотрицательное целое |
--locale <loc> | Переопределяет локаль (по умолчанию en) |
--now <date> | Фиксирует часы, по которым читают today, now и b_day |
--data-path <dir> | Добавляет папку данных для @data/… (можно повторять) |
--jobs <n> | Число рабочих потоков, положительное целое (по умолчанию TDC решает сам) |
--mode <memory|disk> | Движок: disk (по умолчанию) или memory |
--engine <1|2|3> | Форсировать конкретный движок (для продвинутых) |
--disk | То же, что --mode disk — и так по умолчанию |
--progress | Пишет <output>.progress — небольшой JSON-файл состояния (нужен -o) |
--stream | Легаси-алиас для --engine 2 |
-h, --help | Показывает помощь |
-v, --version | Показывает версию |
Длинные опции можно писать и через =: tdcv2 demo.tdc --output=out.csv --count=100.
В примерах ниже используется вот такой demo.tdc:
<tdc>
<env count="10" seed="demo" local="en">
<sequence name="Id"><gen type="increment" value="1"/></sequence>
<sequence name="City"><gen type="text" value="Moscow,Berlin,Paris" order="sequential"/></sequence>
<sequence name="Status"><gen type="text" value="new,active,closed"/></sequence>
<before><line><data>Id,City,Status</data></line></before>
</env>
<block><line><data>${{Id}},${{City}},${{Status}}</data></line></block>
</tdc>
Id,City,Status 1,Moscow,closed 2,Berlin,new 3,Paris,closed 4,Moscow,new 5,Berlin,closed 6,Paris,new 7,Moscow,new 8,Berlin,active 9,Paris,active 10,Moscow,active
--seed — переопределить случайность
В конфиг зашит один сид, а вам нужен другой набор значений — не трогая файл.
--seed переопределяет его: столбцы со счётчиком (Id) и с перебором по кругу
(City) от сида не зависят, поэтому меняется только Status.
--count — сколько строк
--count 4 отрисует четыре строки. Позиционные столбцы (счётчик, текст по кругу) —
это префикс; столбцы с точными долями (percent, <mix>) и с уникальностью (uniq)
пересчитываются от нового общего количества.
См. Детерминизм и пропорции.
--output — записать в файл
-o (или --output) пишет результат в файл; в stdout при этом ничего не выводится:
tdcv2 demo.tdc -o out.csv
--locale — язык шаблонных данных
Шаблонные генераторы (имена, города) по умолчанию выдают английские данные;
--locale ru переключает весь файл на русский, позиция в позицию.
--now — зафиксировать часы
Часть генераторов смотрит на часы: value="today", value="now", person.b_day
(возрастное окно, отсчитанное назад от сегодня) и генератор date, которому не задали
границ. Так и задумано — день рождения, который едет вслед за сегодняшним днём, в этом и
смысл. Но часы становятся входом прогона наравне с конфигом и сидом — и единственным
входом, который нельзя записать. Тот же файл с тем же сидом завтра выдаст другие
строки.
--now записывает их:
tdcv2 people.tdc --seed demo --now 2026-04-23 -o out.csv
Запустите это через год — получите те же байты. Уберите флаг — и прогон возьмёт реальные часы, что и нужно в продакшене и не нужно в тесте.
Значение — дата в том же синтаксисе, который принимает <gen type="date" value="…">:
2026-04-23 или 2026-04-23T09:30:00, когда важен час. Часового пояса нет: все даты в
TDC — UTC. Значение, которое TDC прочитать не может, — это ошибка, а не молчаливый
возврат к реальным часам:
tdcv2: invalid --now "yesterday" — expected YYYY-MM-DD or YYYY-MM-DDTHH:mm:ss (UTC)
--data-path — внешние данные
Когда конфиг читает src="@data/…", CLI нужно знать, где лежит папка data/. Она
передаётся через --data-path (опцию можно повторять — папки просматриваются по порядку):
tdcv2 demo.tdc --data-path ./data --data-path ./private-data -o out.csv
Обычный относительный путь src="names.txt" сначала ищется рядом с .tdc-файлом,
затем в папках из --data-path.
Скорость и движки — --jobs, --mode, --engine
Обычно ни один из этих флагов не нужен: TDC сам выбирает движок по конфигу и сам решает, распараллеливать ли генерацию. Коротко:
-
--jobs N— задать число рабочих потоков вручную. Это только про скорость: вывод байт-в-байт совпадает с однопоточным прогоном. -
--mode memory— маленький in-RAM движок (аварийный выход для небольших данных и объектного API). Значения он даёт те же самые, что и остальные движки (детерминированность); он просто держит каждую колонку в памяти, а не выдаёт её потоком. -
--engine 1|2|3— форсировать конкретный движок;--stream— легаси-алиас для--engine 2.--modeописывает стоимость, и такой прогон по-прежнему может уйти на другой движок.--engine 2отказывается от всего, что не может считать потоком, — поэтому замер меряет то, что называет.--engine 3отказывается ровно в одном случае — когдаuniqтеснее, чем вытягивает его ограниченная починка, — а в остальных откатывается на движок в памяти и печатает его байты: код 0 и ни слова. Это сужено намеренно: формы, на которых движок 3 откатывается, — ровно те, которых ленивый путь вообще не выражает, и покрывать их и есть его работа. Но значит и вот что: замер памяти с--engine 3наstat, running-сумме или простомuniq— это замер движка 1. Какой движок запустит ваш конфиг перечисляет формы.
TDC сам прикидывает, сколько потоков поместится в оперативку этой машины, и берёт столько — на слабой машине запуск просто пойдёт медленнее, но не упадёт на середине. Подробнее — в разделе Большие объёмы.
--progress — наблюдать за долгим прогоном
Прогон на сто миллионов строк долго молчит, а молчание выглядит ровно как зависание.
--progress пишет рядом с выводом небольшой JSON-файл — <output>.progress — и
переписывает его примерно раз в секунду:
tdcv2 demo.tdc -o out.csv --progress
{
"phase": "render",
"done": 4200000,
"total": 10000000,
"percent": 42,
"startedAt": 1787871050458,
"updatedAt": 1787871083822,
"pid": 51234
}
startedAt и updatedAt — миллисекунды от эпохи. Смотреть стоит на updatedAt: он
двигается при каждой записи, так что живой прогон отличается от остановившегося без
обращения к файловой системе за временем правки.
Каждое обновление пишет <output>.progress.tmp и переименовывает поверх
<output>.progress, так что читатель никогда не застанет наполовину записанный файл.
.tmp виден рядом с выводом, пока идёт прогон. tdcv2 format -w делает так же с
<file>.tmp.
Первая запись — {"phase":"starting"}, ещё до того, как у работы появится хоть одно
число. Она нужна, чтобы файл СУЩЕСТВОВАЛ с первого мгновения: тот, кто файла не нашёл, не
отличит «ещё не начали» от «умерло», а запуск десятка воркеров на большом конфиге занимает
секунды.
Дальше фазы — в этом порядке, когда случаются: uniq-scan (хешируется набор значений каждой строки), uniq-sort
(сортируются кучи), uniq-repair (проверяются и переставляются повторившиеся наборы) и
render (пишутся строки).
Какие из них сообщит прогон — зависит от движка и заранее не известно. Замерено на одном
конфиге с <uniq>: движок в памяти сообщает только render; потоковый на 300 000 строк —
uniq-repair, затем render; тот же конфиг на 1 500 000 строк, где прогон разбивается по
воркерам, — все четыре. И план не закреплён даже после старта: потоковый движок может
встретить конфиг, который ему не выразить, сдаться на полпути и отдать весь прогон движку в
памяти, а тот сообщит render и больше ничего.
Поэтому в файле нет ЧИСЛА фаз. «Фаза 2 из 4», объявленная в начале, была бы числом, до которого этот прогон может и не дойти, а полоса, построенная на нём, прыгала бы — чего полосе делать нельзя. Рисуйте фазу и её собственные числа: они верны всегда.
У uniq-repair на
параллельном прогоне нет done/total — расстановка там считается одним вызовом, а не
шагами, которые можно пересчитать, — поэтому сообщается одна фаза. На большом
uniq-прогоне ни одна из них не перевешивает: на 6 000 000 строк по 900 000 000 возможных
пар запись строк заняла 17 секунд, хеширование каждого набора — 12, сортировка куч — 3, а
ремонт — 7, всего около 40.
Внутри фазы числа только растут, и фаза заканчивается на своём же итоге — поэтому
полоса,
нарисованная по ним, не прыгает назад и не замирает, не дойдя до конца. uniq-repair — это
несколько разных по смыслу шагов на одной растущей шкале, поэтому её итог — это то, сколько
работы ремонт набрал на себя к этому моменту, а не заранее известное число.
Последняя запись — {"phase":"done","percent":100,...} с числом секунд, которое
занял прогон.
Опрашивать файл безопасно по двум причинам. Он заменяется атомарно, поэтому читающий
никогда не увидит половину JSON. И он переписывается не реже раза в секунду — то же
состояние заново, со свежим updatedAt, — независимо от того, есть ли работе что сказать.
Поэтому файл, который не двигается несколько минут, означает, что процесс умер, что бы ни
было написано внутри.
Этот пульс — по мере сил, и об этом стоит знать точно. Таймер живёт в том же процессе,
поэтому непрерывный кусок вычислений может его придержать: на прогоне в 1 500 000 строк с
<uniq> самая долгая тишина составила 10.9 секунды, во время ремонта. До того, как таймер
появился, такой же прогон молчал 2 минуты 16 секунд, будучи совершенно здоровым, — этого
хватило бы, чтобы читатель этой страницы объявил его мёртвым. Судить о жизни по минутам, а
не по секундам, — и правило работает.
Нужен -o: файл состояния лежит рядом с выводом, а без вывода его некуда положить — и
команда говорит об этом, а не принимает флаг и молча его теряет.
Прогон, разбитый на несколько воркеров, считается целиком. Каждый воркер сообщает, сколько строк написал, а координатор складывает — процент относится ко всему файлу, а не к одному воркеру. Это важно: свыше ста тысяч строк TDC делит прогон сам, если не сказать иначе.
Вывод в Parquet тоже отчитывается — раз на группу строк, то есть раз на пятьдесят тысяч. Грубее, чем в текстовом пути, и намеренно: группа строк — это единица, которой работает тот писатель, и внутри неё нет момента, где незаконченная группа что-то значит. Если процент дойдёт до конца и начнётся заново — значит прогон считается дважды; это стоит завести как ошибку, а не терпеть.
Тот же канал есть в библиотеке во всех реализациях — как обратный вызов с аргументами
(phase, done, total).
tdcv2 check
Читает конфиг, проверяет его и ничего не генерирует. То, что нужно в pre-commit-хуке или в CI: отвечает на вопрос «а запустится ли это?», не тратя времени на сам запуск.
tdcv2 check demo.tdc
Всё уходит в stderr — на верный конфиг одна строка, на неверный те же диагностики, что напечатала бы обычная генерация. В stdout не уходит ничего, и это намеренно: stdout хука — это шум, а тот, кому нужны данные, запускает генератор.
tdcv2: demo.tdc is valid
Предупреждения проверку не заваливают — они печатаются, а код выхода остаётся 0, потому
что предупреждение описывает то, что работает, но, скорее всего, задумывалось иначе.
Кодом 1 завершается только ошибка.
--brief — единственный флаг у check. Он печатает по одной строке на диагностику (код,
позиция, сообщение, подсказка) без выдержки из исходника — для редакторов, CI и всего
остального, что вывод читает, а не разглядывает:
TDC041 1:70 unknown gen type "nosuch" :: Allowed types: text, file, template, number, regex, advanced_regex, … (11 more).
tdcv2 format
Приводит .tdc в аккуратный вид — отступы, пробелы в атрибутах, выровненные таблицы
<map> — тот же форматтер, что и в редакторе.
tdcv2 format demo.tdc # печатает отформатированный конфиг в stdout
tdcv2 format -w demo.tdc # переписывает файл на месте (-w / --write)
Форматирование никогда не меняет то, что генерирует конфиг. Синтаксическая ошибка будет показана, а файл останется нетронутым (код выхода 1).
Коды выхода
| Код | Значение |
|---|---|
0 | Успешная генерация, --help или --version |
1 | Ошибка чтения, парсинга, валидации или выполнения |
2 | Неправильные аргументы CLI — а также любой сбой pack или init (скачивание, контрольная сумма, уже существующий конфиг) |
См. также
- Установка пакетов данных —
tdcv2 init,tdcv2 pack. - Большие объёмы —
--jobs,--mode,--engineподробно.