SQLite made scriptable for shell programmers.
A tiny, practical, native C++23 CLI for people who are tired of using fragile
.txt files as databases in shell scripts.
FrogDB is a command-line SQLite tool designed for shell programmers.
It gives Bash, Shell and automation scripts a clean database interface without forcing you to write raw SQL for every tiny task.
Instead of this:
grep "^victor:" users.txt | cut -d: -f2Use this:
EMAIL=$(frogdb app.db get users.email name=Victor --raw)
echo "$EMAIL"FrogDB is for that moment when a .txt, .csv, .conf or improvised log file has become too fragile, but using a full database client still feels too heavy.
It is intentionally simple:
one binary
one SQLite file
shell-friendly output
scriptable commands
no database server required
Recommended repository description:
SQLite made scriptable for shell programmers.
Alternative slogan:
Stop grepping text files. Query your scripts.
- Native C++23 command-line tool.
- SQLite backend by default.
- Optional encrypted database support through SQLCipher.
- Built for Bash/Shell automation.
- Output modes for humans and scripts:
- table
- raw
- JSON
- CSV
- env
- Easy shell variable extraction.
- Prepared statements for generated SQL values.
- Identifier allowlist for table and column names.
- Protection against accidental mass update/delete.
- Simple backups.
- MIT licensed.
- Source-only distribution: compile locally on your own machine.
Text files are fine until they are not.
This is okay:
echo "victor:victor@example.com" >> users.txtUntil you need:
spaces
quotes
commas
newlines
unique values
updates
deletes
filters
counts
exports
safe parsing
real structure
Then your script becomes a pile of grep, cut, awk, sed, escaping bugs and sadness.
FrogDB lets your shell script use SQLite without turning your script into a SQL client wrapper nightmare.
Clone the repository:
git clone https://github.com/victormeloasm/frogdb.git
cd frogdbInstall dependencies on Ubuntu/Debian:
sudo apt update
sudo apt install clang lld make libsqlite3-dev sqlite3Build:
makeCheck:
./frogdb --versionInstall system-wide:
sudo make installNow you can run:
frogdb --versionUninstall:
sudo make uninstallFrogDB works as a normal SQLite CLI by default.
If you want encrypted database files, build with SQLCipher:
sudo apt update
sudo apt install clang lld make sqlcipher libsqlcipher-dev
make clean
make sqlcipherCheck:
./frogdb --versionExpected output:
frogdb 0.2.0 sqlcipher
Install the SQLCipher-enabled build:
sudo make install-sqlcipherIf make says something like:
make: aviso: O arquivo 'Makefile' está com a hora adiantada
make: aviso: O relógio está errado. Sua compilação pode ficar incompleta.
Touch the extracted files and rebuild:
find . -exec touch {} +
make clean
makeFor SQLCipher:
find . -exec touch {} +
make clean
make sqlcipherIf it keeps happening, fix your system clock:
timedatectl
sudo timedatectl set-ntp trueCreate a database:
frogdb init app.dbCreate a table:
frogdb app.db table users \
id:int:pk:auto \
name:text:notnull \
email:text:unique \
age:int \
created_at:textInsert rows:
frogdb app.db insert users \
name="Victor" \
email="victor@example.com" \
age=30 \
created_at="$(date -Is)"
frogdb app.db insert users \
name="SapoGPT" \
email="sapo@example.com" \
age=9000 \
created_at="$(date -Is)"List rows:
frogdb app.db list usersFind rows:
frogdb app.db find users name=VictorGet a single field:
frogdb app.db get users.email name=Victor --rawUse it in a shell variable:
EMAIL=$(frogdb app.db get users.email name=Victor --raw)
echo "$EMAIL"The heart of FrogDB is shell scripting.
Use --raw when you want a clean value:
NAME=$(frogdb app.db get users.name id=1 --raw)
EMAIL=$(frogdb app.db get users.email id=1 --raw)
echo "$NAME <$EMAIL>"Use env when you want shell assignments:
frogdb app.db env users id=1Example output:
id='1'
name='Victor'
email='victor@example.com'
age='30'
created_at='2026-06-05T20:00:00-03:00'Load it into your current shell:
eval "$(frogdb app.db env users id=1)"
echo "$name"
echo "$email"FrogDB shell-escapes values for this mode.
Best for humans:
frogdb app.db list users --tableBest for variables and pipes:
frogdb app.db get users.email id=1 --rawBest for tools like jq:
frogdb app.db list users --jsonExample:
frogdb app.db list users --json | jq .Best for spreadsheets or export:
frogdb app.db export users --csv > users.csvfrogdb init <db>Example:
frogdb init app.dbfrogdb <db> table <table> <column:type[:flags]>...Example:
frogdb app.db table users \
id:int:pk:auto \
name:text:notnull \
email:text:unique \
age:intSupported type aliases:
int
integer
text
real
blob
numeric
datetime
bool
Supported flags:
pk
auto
notnull
unique
Column example:
id:int:pk:auto
email:text:unique
name:text:notnull
frogdb <db> insert <table> key=value ...Example:
frogdb app.db insert users name="Victor" email="victor@example.com" age=30frogdb <db> list <table>Example:
frogdb app.db list usersWith filter:
frogdb app.db list users name=VictorWith limit:
frogdb app.db list users --limit 10frogdb <db> find <table> key=value ...Example:
frogdb app.db find users email="victor@example.com"frogdb <db> get <table.column> key=value ... --rawExample:
EMAIL=$(frogdb app.db get users.email name=Victor --raw)frogdb <db> env <table> key=value ...Example:
eval "$(frogdb app.db env users name=Victor)"Use --where for safe updates:
frogdb app.db set users age=31 --where name=VictorFrogDB refuses mass updates by default:
frogdb app.db set users status=doneTo update all rows intentionally:
frogdb app.db set users status=done --all --yesDelete with filter:
frogdb app.db del users name=VictorFrogDB refuses deleting everything by default:
frogdb app.db del usersTo delete all rows intentionally:
frogdb app.db del users --all --yesfrogdb app.db count usersWith filter:
frogdb app.db count users age=30frogdb app.db tablesfrogdb app.db schemaFor one table:
frogdb app.db schema usersfrogdb app.db sql "SELECT id, name, email FROM users WHERE age >= 18" --tableJSON:
frogdb app.db sql "SELECT * FROM users" --jsonCSV:
frogdb app.db sql "SELECT * FROM users" --csvRaw SQL mode is intentionally raw. Use it carefully.
frogdb app.db export users --csv > users.csv
frogdb app.db export users --json > users.jsonfrogdb app.db backupOr choose the backup path:
frogdb app.db backup app.db.bakSQLite itself does not provide password encryption in the standard library.
FrogDB supports encrypted database files when compiled with SQLCipher:
make sqlcipherCreate encrypted DB:
frogdb --ask-key init secrets.db --encryptedCreate table:
frogdb --ask-key secrets.db table secrets \
id:int:pk:auto \
name:text:unique \
value:textInsert secret:
frogdb --ask-key secrets.db insert secrets name=token value="abc123"Read secret:
TOKEN=$(frogdb --ask-key secrets.db get secrets.value name=token --raw)
echo "$TOKEN"Interactive prompt:
frogdb --ask-key secrets.db list secretsEnvironment variable:
export FROGDB_KEY='strong password here'
frogdb --env-key secrets.db list secretsKey file:
printf '%s' 'strong password here' > .frogdb_key
chmod 600 .frogdb_key
frogdb --key-file .frogdb_key secrets.db list secretsDirect key:
frogdb --key 'strong password here' secrets.db list secretsDirect --key works, but it is discouraged because it can leak through shell history or process lists.
Instead of:
echo "theme=dark" >> config.txt
grep '^theme=' config.txt | cut -d= -f2Use:
frogdb config.db table settings key:text:pk value:text
frogdb config.db insert settings key=theme value=dark
THEME=$(frogdb config.db get settings.value key=theme --raw)
echo "$THEME"frogdb jobs.db table jobs \
id:int:pk:auto \
name:text:notnull \
status:text \
created_at:text
frogdb jobs.db insert jobs \
name=backup \
status=pending \
created_at="$(date -Is)"
JOB_ID=$(frogdb jobs.db get jobs.id name=backup --raw)
frogdb jobs.db set jobs status=done --where id="$JOB_ID"frogdb inventory.db table assets \
id:int:pk:auto \
hostname:text:unique \
ip:text \
role:text
frogdb inventory.db insert assets hostname=server01 ip=10.0.0.10 role=web
frogdb inventory.db insert assets hostname=server02 ip=10.0.0.11 role=db
frogdb inventory.db find assets role=web --jsonfrogdb app.db list users --csv | while IFS=, read -r id name email age; do
echo "$id -> $email"
doneFor more robust CSV parsing, use a CSV-aware tool.
FrogDB is designed for local automation.
Generated commands use prepared statements for values.
Table and column names are validated using a strict identifier allowlist:
[A-Za-z_][A-Za-z0-9_]*
This protects generated commands from obvious SQL injection through identifiers.
The sql command is raw SQL by design. It is for users who intentionally want direct SQL access.
Encrypted database support requires SQLCipher. A normal SQLite build does not encrypt the database.
FrogDB exists because shell scripts deserve better than fragile text parsing.
The goal is not to replace SQLite.
The goal is to make SQLite feel natural from shell scripts:
value=$(frogdb db get table.column key=value --raw)Small. Fast. Native. Scriptable. Useful.
MIT License.
Created by Víctor Duarte Melo.
