Pular para o conteúdo principal

Um lote de pessoas

botai pessoas -n N [--semente S] [--hoje AAAA-MM-DD] [--uf UF] [--dominio-email D]
[--formato json|ndjson|csv|sql] [--dialeto postgres|mysql|sqlite]
[--tabela T] [--campos a,b,c]
OpçãoO que faz
-n Nquantas pessoas, de 0 a 100 000; obrigatório
--semente Sa pessoa i do lote usa a semente S/i
--hoje AAAA-MM-DDdata de referência
--uf UFsigla da UF de todos os endereços
--dominio-email Ddomínio dos e-mails
--formato Fjson (padrão), ndjson, csv ou sql
--dialeto Dpostgres (padrão), mysql ou sqlite; só com --formato sql
--tabela Ttabela do INSERT (padrão pessoas; aceita esquema.tabela); só com sql
--campos a,b,ccolunas do csv e do sql, nesta ordem

--semente, --hoje, --uf e --dominio-email funcionam como no botai pessoa.

Sem repetição, com prefixo estável​

Num lote de até 100 000 pessoas, nenhum CPF, e-mail ou CNPJ se repete:

botai pessoas -n 100000 --semente carga --hoje 2026-10-05 --formato csv --campos cpf,email,empresa_cnpj > lote.csv
tail -n +2 lote.csv | awk -F, '{ cpf[$1]; email[$2]; cnpj[$3] } END { print length(cpf), length(email), length(cnpj) }'
100000 100000 100000

As primeiras k pessoas de um lote de n são o lote de k. Dá para aumentar o -n sem mudar as pessoas que você já usa:

diff <(botai pessoas -n 3 --semente demo --hoje 2026-10-08 --formato ndjson) \
<(botai pessoas -n 10 --semente demo --hoje 2026-10-08 --formato ndjson | head -n 3)

A unicidade vale só para CPF, e-mail e CNPJ, e só dentro do mesmo lote. Os nomes repetem. Como o lote chega a isso (S/i, S/i/2…) está em Lote e unicidade.

informação

Com --uf, todos os endereços saem daquela UF. Em 24 das 27 UFs há um CEP só, e o lote inteiro recebe o mesmo endereço. Veja Limites.

json​

O padrão. Um envelope só, com as pessoas no array pessoas:

botai pessoas -n 3 --semente demo --hoje 2026-10-08 | head -n 12
{
"formato": 1,
"motor": "0.4.1",
"semente": "demo",
"hoje": "2026-10-08",
"pessoas": [
{
"nome": {
"sexo": "F",
"prenome": "Isabela",
"sobrenomes": [
"Freitas",

O json monta o lote inteiro na memória antes de escrever: com 100 000 pessoas, cerca de 1,5 GB. Para lote grande, use ndjson, csv ou sql, que escrevem aos poucos (cerca de 130 MiB com 100 000).

ndjson​

Um envelope por linha, cada um com a semente exata da pessoa:

botai pessoas -n 3 --semente demo --hoje 2026-10-08 --formato ndjson | grep -o '"semente":"[^"]*"'
"semente":"demo/0"
"semente":"demo/1"
"semente":"demo/2"

Com essa semente, o botai pessoa recria só aquela pessoa. Quando uma pessoa é sorteada de novo para não repetir CPF, e-mail ou CNPJ, a linha diz qual semente valeu:

botai pessoas -n 1000 --semente mil-3 --hoje 2026-10-05 --formato ndjson | grep -o '"semente":"mil-3/971/[^"]*"'
"semente":"mil-3/971/2"

csv​

CSV no padrão RFC 4180: cabeçalho na primeira linha, linhas terminadas em CRLF e null como campo vazio.

botai pessoas -n 3 --semente demo --hoje 2026-10-08 --formato csv --campos nome,cpf,email
nome,cpf,email
Isabela Freitas Santos,550.160.642-96,isabela-santos-7825@tuamaeaquelaursa.com
Vitória Alves Carvalho,843.495.439-70,vitoria-carvalho-1721@tuamaeaquelaursa.com
Lucas Gabriel Pereira Oliveira,750.346.866-19,lucas-oliveira-9018@tuamaeaquelaursa.com

O CSV não traz a semente. Sem --semente, a CLI escreve a semente sorteada no stderr, para você reproduzir o lote:

botai pessoas -n 50 --formato csv --campos nome,cpf,email > p.csv
botai: semente 75bf48f551f1dd10, hoje 2026-10-08

sql​

Um INSERT por pessoa. A 1ª linha é um comentário com o formato, o motor, a semente e o hoje. idade sai como número e null como NULL.

botai pessoas -n 2 --semente demo --hoje 2026-10-08 --formato sql --campos nome,cpf,idade
-- botai: formato 1, motor 0.4.1, semente demo, hoje 2026-10-08
INSERT INTO "pessoas" ("nome", "cpf", "idade") VALUES ('Isabela Freitas Santos', '550.160.642-96', 50);
INSERT INTO "pessoas" ("nome", "cpf", "idade") VALUES ('Vitória Alves Carvalho', '843.495.439-70', 58);

O SQL traz só os INSERTs, sem CREATE TABLE: crie a tabela antes. As receitas completas estão em Receitas de banco de dados.

--dialeto​

postgres (o padrão), mysql ou sqlite. O Postgres e o SQLite saem iguais byte a byte:

cmp <(botai pessoas -n 1000 --semente carga --hoje 2026-10-05 --formato sql --dialeto postgres) \
<(botai pessoas -n 1000 --semente carga --hoje 2026-10-05 --formato sql --dialeto sqlite)

Só o MySQL muda: crases em vez de aspas duplas e escape de barra invertida.

botai pessoas -n 2 --semente demo --hoje 2026-10-08 --formato sql --dialeto=mysql --campos nome,cpf,idade
-- botai: formato 1, motor 0.4.1, semente demo, hoje 2026-10-08
INSERT INTO `pessoas` (`nome`, `cpf`, `idade`) VALUES ('Isabela Freitas Santos', '550.160.642-96', 50);
INSERT INTO `pessoas` (`nome`, `cpf`, `idade`) VALUES ('Vitória Alves Carvalho', '843.495.439-70', 58);

O --dialeto diferencia maiúsculas, ao contrário do --uf: MySQL é erro de uso.

--tabela​

O nome da tabela, ou esquema.tabela, com letras, dígitos e _, até 63 caracteres:

botai pessoas -n 2 --semente demo --hoje 2026-10-08 --formato sql --tabela app.clientes --campos nome,cpf,idade
-- botai: formato 1, motor 0.4.1, semente demo, hoje 2026-10-08
INSERT INTO "app"."clientes" ("nome", "cpf", "idade") VALUES ('Isabela Freitas Santos', '550.160.642-96', 50);
INSERT INTO "app"."clientes" ("nome", "cpf", "idade") VALUES ('Vitória Alves Carvalho', '843.495.439-70', 58);

Qualquer outra coisa é recusada antes de gerar o SQL, o que protege contra injeção:

botai pessoas -n 10 --formato sql --tabela 'x;drop'
botai: tabela inválida "x;drop" (letras, dígitos e _, até 63 caracteres; opcionalmente esquema.tabela)

a.b.c também sai com 2.

--campos​

Escolhe e ordena as colunas do csv e do sql. São 33 colunas; sem --campos, saem todas, na ordem da referência de colunas. Em json e ndjson, --campos é erro de uso: lá sai a pessoa inteira.

botai pessoas -n 10 --campos nome
botai: --campos só vale com --formato csv ou sql

Tempo e tamanho​

Medido em 2026-10-08, num macOS arm64 com Node 22.22.3 (máquina compartilhada, com variação):

LoteTempo
10 0000,25 a 0,29 s, em qualquer formato
100 0001,8 a 2,1 s (ndjson: 1,84 a 1,88 s)
Formato, com 100 000 pessoasTamanhoMemória
json162 MiB~1,5 GB
ndjson115,2 MiB~130 MiB
csv44,9 MiB~130 MiB
sql98,7 MiB~130 MiB

Os outros números estão em Números.