Show HN: SQLite query inside a Bash function
Part of the workflow for building my website is the generation of a table in
tab-separated column format (.tsv). The source data is found in four other .tsv
files. I use an SQLite query to perform a 4-way join and write out the new table. For
convenience, I wrote a script that encapsulates the query inside a Bash function.
The example below illustrates this technique.
repertoire() {
pushd $CMM_SOURCES/_data
sqlite3 <<EOS
.headers on
.mode tabs
.import category.tsv category
.import composition.tsv composition
.import concert.tsv concert
.import program.tsv program
.once repertoire.tsv
SELECT
category.name AS category,
composition.key,
composition.composer,
composition.name AS composition,
concert.name AS concert
FROM
category,
concert,
composition,
program
WHERE
julianday(concert.date) < julianday('now')
AND composition.category = category.name
AND program.key = composition.key
AND program.date = concert.date
ORDER BY
category.sequence,
composition.key
;
EOS
popd
}
63 comments
[ 5.8 ms ] story [ 115 ms ] threadOne nitpick with textql is that it says "sqlite import will not accept stdin, breaking unix pipes. textql will happily do so.", but that's not true, you just need to tell it to use stdin by doing:
I do this all the time for using gzip and sqlite to get compressed import with progress. For example filtering a compressed CSV from a SQL query with progress for import:However, having written hundreds of lines of bash gluing systems together for various CI jobs and automations, at some point you need to stop writing bash and switch to something with a richer data model and easier to maintain/test.
It's usually meant as "raises the question", but language lawyers insist it means what the wiki page said.
But this is neither of those two. And if you're the only one who uses a phrase a certain way, and it means other things to everyone else, then that's actually wrong, even in a descriptive language.
More thoughts on this: https://blog.habets.se/2021/06/The-uselessness-of-bash.html
Another piece of evidence is that I do many many code reviews. Not once have I reviewed bash script changes that did not have subtle bugs that would bite us in real life. Not once!
If I'm using bash, I just need to glue a few tools together and I've most likely decided already that concurrency more complex than simple wait(1p) is unnecessary, and that fine-grained error handling is unimportant.
If I really cared about those things, I'd probably use something like Haskell that actually improves on error handling, unlike Go, or if I'm scripting something for a specific software project, whatever that project's language is.
But sure, my choice is Go. You do haskell if it's best for you.
NodeJS and Python could be contenders but Bash has them beat in longevity.
If I’m just writing small tools that wire a few basic commands together bash is fine. For anything that actually needs data to be crunched or decent error handling Python, Go, Perl (though also cryptic), C++ are, IMO, a far better choice.
I did recently have a case where I needed to run a shell command with a multi-line output, and an easy call to os.popen() wasn't returning the full output. So, back to Bash arrays and weird text parsing syntax (IFS=" " read tempArray...) I went...
Bash/shell is great once you realize it's a glue language. You avoid doing anything but pipelines, and if theres no command/tool to do something, you can write that in other language and include it in the pipeline.
Here's a helpful example from SO: https://stackoverflow.com/a/21759264/711585
You could also read the contents of the query file into a shell parameter with the `read` builtin.
I do agree, though, with some of the other comments that you will probably want a "real language" other than bash if this grows at all. Passing in string data for a where clause, for example, would get cumbersome and error prone.
Python or Perl would be good choices if it goes there.
https://tldp.org/LDP/abs/html/here-docs.html
This is only one function. We cannot see the rest of the script. Maybe there is a compelling reason1 to to use pushd and popd instead of using sh features.
1. i.e., no way to achieve same result in sh
https://raw.githubusercontent.com/chmaynard/Sources/core/wor...
I haven’t seen many places with sh but not full bash (busybox being the main exemptions but that breaks most things due to muslc anyway).
It is possible that many shell script authors do not understand the difference between a login shell (optimised for interactive use) and a scripting shell (optimised for non-interactive use). Thus the comparison could be worthwhile. sh is faster, more portable and arguably presents fewer potential gotchas. FWIW, shellschock affected bash but not sh.
NetBSD uses ash-derived sh as both login and scripting shell. No bash. Debian and many, many other GNU/Linux distributions (including musl-based Void) all use NetBSD-derived sh called "dash" as the default scripting shell. I am not aware of any Berkeley distribution that uses bash as the default scripting shell.
The default scripting shell might be important for typing like X11 startup scripts, but I don't see why you have to use it for standalone scripts just because it's the default.
Why? Because they are programming languages that have features that I want.
Bash has more features and functionality than Zsh. It's also pretty much ubiquitous. If your scripts are only meant to run on your computer, and you don't need them to work under Busybox or whatever, why constrain yourself to the limited set of features in POSIX sh?
is it likely you would be sharing them with others on the web.
Thank you for sharing your story. Here's mine. I write scripts in sh. Why? Because it's small, fast and portable. On the OS I use it is not just "pretty much ubiquitous" it is ubiquitous. I have multiple computers running different OS and I want scripts that work on all of them. Some of these computers are resource-constrained and installing additional shells and other interpreters plus libraries is not worth the cost. Anyway, there is no need to do that.
If I need scripts that are small, fast and portable, why should I use zsh plus perl or python.
As I'm writing this I'm thinking that I should just run o=some tests using both approaches, but, how's the performance for you? Any noticeable delays or something of the sort?
For features such as:
Anyone else interests in such approach. Anyone thinks of any potential issue for such integration?If there are enough interests, I can start a open source project for this.
1) Add sqlite_log.[ch] files to bash source repo. 2.a) Add sqlite_init_db(...) to init a database per bash_sqldb_{date}_{pid}.db per bash pid on ~user/.bash_db/ dir. 2.b) Add sqlite_log_cmd(...) to log the cmd, evn, pid, to the opened db. 2.c) have the bash command exec routine to call the sqlite_log_cmd(...) on each cmd execution. 3) Query, report of the commands execution will be handled outside with bash scripts/sub commands/web intf.
* Should be very low overhead (micro-seconds) for each command.
Now I think this out a bit more, maybe it is better to write ebpf script and pipe all the cmds exec to one centralized sqlitedb (instead of per user/pid and only for bash). Likely more useful from system security auditing POV, easier to extend by adding network connetions, file open, privilege escalation type events to the DB.