Miller CLI – Like Awk, sed, cut, join, and sort for CSV, TSV and JSON
github.com
github.com
$ sqlite3
$ .mode csv
$ .import my.csv foo
$ SELECT * FROM foo WHERE name = 'bar';
It reads the header in automatically for the field names and then stores all the values as strings.Another advantage is that this can be executed to import as part of initialization and get to sqlite prompt with the table ready for sql execution:
$ csv_to_table.sh sample-test.csv
Or it can be executed using heredoc for non-interactive execution as well: $ csv_to_table.sh sample-test.csv <<-CMD
select * from sample_test limit 10;
CMD
... output ... PS> gsudo choco install sqlite3
PS > sqlite3
sqlite> .mode csv
sqlite> .separator ; \n
sqlite> .import my.csv foo
sqlite> SELECT * FROM foo WHERE name = 'bar';
For CSV that contain up to several 100k records native PowerShell solution is way faster and more practical: Get-Content my.csv | ConvertFrom-Csv | ? name -eq bar
FYI, I maintain miller choco package: https://community.chocolatey.org/packages/millerThe first example in PowerShell:
> Import-Csv example.csv | Sort-Object Color, Shape
To filter, use `Where-Object`:
> Import-Csv example.csv | Where-Object { $_.Color -eq 'purple' } | Sort-Object Color, Shape
Or using aliases:
> ipcsv example.csv | ? { $_.Color -eq 'purple' } | sort Color, Shape
JSON uses the same syntax -- just replace `Import-Csv` with `Get-Content` and `ConvertFrom-Json`:
> Get-Content example.json | ConvertFrom-Json | Select-Object Color -Unique
> purple
> red
> yellow
There's also `Group-Object` for aggregation.
csvkit, a set of command line tools for manipulating csvs: https://csvkit.readthedocs.io/en/latest/
visdata, a quite terminal based csv explorer: https://www.visidata.org/
https://github.com/capitalone/dataprofiler
Can Install: pip install dataprofiler[ml] --user
How it works:
csv_data = Data('your_file.csv') # Load: delimited, JSON, Parquet, Avro
csv_data.data.head(10) # Get head
csv_data.data.sort_values(by='name', inplace=True) # Sort
Anyway, pretty interesting Miller CLI. I'm not sure how the header detection works, especially if the header isn't the first row (which is often the case)
You might find it useful.
If you are taking feature request, string interpolation or a sprintf function would make report generation kind of things easier for me.
* NQP
* Rakudo
* Rakudo programs
Building on https://yakshavingcream.blogspot.com/2019/08/summer-in-revie...
Aside for that it appears to support more formats (CSV [which has partial JQ support], and TSV), of course.
I find it very helpful when people compare their new toy or service with what is already pretty well known.
It is somewhat similar to JQ, but specifically for record based formats and not documents.
The example page [0] has quite a few nice examples.
A recent snippet from my own usage, looking for duplicate ids in a large-ish csv.gz:
mlr uniq -c -g id then filter '$count > 1'
mlr --ijson --ocsv filter '$success' then stats1 -a mean,min,max,stddev -f duration
[0]: https://miller.readthedocs.io/en/latest/data-examples.htmlEven the tutorial leans on sed for cleansing ahead of Miller. https://www.ict4g.net/adolfo/notes/data-analysis/miller-quic...
The concept of everything as a file, has awesome benefits I believe, but that doesn't meant files need to be unstructured text.
There's a core problem, that with text languages (like AWK or structured regex (sed, etc) you end up both having to parse AND manipulate data. which is no fun and prone to errors.
Abstracting away all of that into codecs that all of coreutils could speak would be very cool to me.
The second issue is structured data vs unstructured text. CSVs, or other table based formats make sense sometimes, and sometimes you want to be able to query things more easily. JSON provides that. What's the synthesis of both I don't know... but maybe this is closer to heaven.
I'd like to _minimize_ the amount of parsing or text manipulation done in any program and be able to focus on extracting and inserting the data I need, like a rosetta stone that just handles everything for me while I work. I want to be able to do:
awk "{print $schoolName}" or awk "{print $4}" and it just works - json, text, CSV.
> In the context of IBM mainframe computers in the S/360 line, a data set (IBM preferred) or dataset is a computer file having a record organization.
> Data sets are not unstructured streams of bytes, but rather are organized in various logical record[3] and block structures determined by the DSORG (data set organization), RECFM (record format), and other parameters
https://en.wikipedia.org/wiki/Data_set_(IBM_mainframe)
Remarkably S/360 predates UNIX by several years, so its definitely not a novel concept. Albeit apparently IBM systems cost order of magnitude more than PDPs of the time.
Of course there are also classic LISP machines where afaik pretty much everything was a SEXPR, but I have even less knowledge about the details of those.
`Import-Csv` and `ConvertFrom-Json` return objects that you can manipulate using LINQ or PowerShell's `*-Object` cmdlets (`Select-Object`, `Where-Object`, `Group-Object`, etc.). `Get-Process` returns objects representing your system's processes, `Get-Service` returns objects for your system's services, and so on.
Being able to chain that in your pipeline is incredibly powerful. You can send data into and out of CSVs and various other providers in a single line. It's a huge productivity boost.
There is no reliable way to infer csv files: CSV files do not have a magic number. There are all kinds of separators used and quoting rules differ widely.
It's super annoying if a tool works on a 1K line CSV file, but breaks down if I have a 3 line file because it can't infer the type.
I much prefer my tools not to be "90%-smart", but predictable.
How can that be? Is it possible to have such an ambiguous file? I mean, if a file contains a single number on a single name it can be anything, but the interpretation is the same. Can you create a file that has different contents depending on whether it is interpreted as csv or tsv?
Easily!
a\tb,c
Is "a\tb" and "c" as a csv, but "a" and "b,c" as a tsv!In practice, if the first line contains only a tab or a comma it might be enough to infer that as the separator, but:
1. that would fail on single-column files (by misinterpreting them as multi-column if the unused separator appears) 2. that couldn't infer anything on files where both separators appear
So it would only be a 90% (or maybe 99%) solution.
The issue with the heuristic is that it can fail depending on the input, and the input can easily change in a way that kills the heuristic.
Say you run the tool on a file, and it detects csv input and all is well. Then you update the file, and now it includes a tab character in the first line and the heuristic detects it as a tsv and now it fails - or the heuristic now gives up, or whatever.
Sure you can "improve the heuristic", but you can still, always, have the data change in a way that it defeats it. You now need to either be careful with the data or _know_ that you should specify the format, without the tool telling you. Everything seems to work immediately, and then later it blows up. That's a problem akin to e.g. bash and filenames with spaces. Everything works, until someone has a space in a filename, and then you get told that you should have known to quote everything all along (the solution there, would be to abolish word splitting).
To coin a pithy phrase: When a tool is easy to misuse and a user misuses it, blame the tool, not the user.
Now, if I were writing this thing I would make the logic much simpler: Make it default to csv (or whichever format is more common). Now the way to break the "heuristic" is to give data in the wrong format. But if you use csv, you don't have to explicitly give the format and your data can't break the heuristic (unless it switches format, which you would know about).
Completely different example, but limits in Splunk aggregations - it means you can run your report on small data, but when you scale it up (to real production data sizes, maybe), then suddenly you get wrong numbers, and maybe results like "0 errors of type X" when the real answer is that there are >=1 errors. Because one of the aggregations used has a window size limit that it is silently applying. This stuff is dangerous.
What Splunk was doing for me would be the equivalent of an SQL join giving approximate answers when the data is too big.
12,000
If it isn't quoted, then a csv will read the comma as a separator, while a TSV won't
amazing number of similar examples. I went for a real use-case rather than a theoretical possibility
Defining every flavour of these is hardly possible with a simple command line so I would rather let the user to specify an entire configuration file for this. We probably need an entire CSV schema language.
But I do agree you could have a heuristic. E.g. ends in .csv and contains a lot more commas/semicolons/tabs than you would expect in normal text in the first 1-5 lines.
You could still have the flag as a fallback when you need something that's completely reliable.
I understand your preference, but please recognize that it is not universal. Some of us much prefer a super-simple tool that fails for some particular cases, while requiring special options to be completely general. Thus you can use the heuristic defaults interactively (where you'll notice the errors easily), and write scripts with the more explicit form.
For example, there's plenty of variation between platforms/applications when it comes to just terminating a line. Are we using CR, LF, CR+LF, LF+CR, NL, RS, EOL? What do we do when the source file is produced by an app that uses one approach but doesn't care about the others (allows their occurrence)?
If those others should appear in the data would our "90%-smart" tool make the wrong determination on line termination for the whole file? would everything just break or would this tool churn along and wreck all the data? how long until you noticed?
By my estimation, the "90%-smart" tool would be about 30% dependable unless used only with a known source and format, meaning it wouldn't need to be smart in the first place.
My point is that supporting "general CSV files" is useless. Restricting your tooling to "simple CSV files" is good. But my opinions are not very representative. I also think that it is perfectly acceptable for a shell script to fail badly when it encounters filenames with spaces.
Oct Dec Hex Char
----------------------------------------
034 28 1C FS (file separator)
035 29 1D GS (group separator)
036 30 1E RS (record separator)
037 31 1F US (unit separator)
https://ronaldduncan.wordpress.com/2009/10/31/text-file-form...So basically Perl? :)
Example usage:
sqlite-utils memory example.csv "select * from t order by color, shape"
# Defaults to outputting JSON, you can add
# --csv or --tsv for those formats or
# --table to output as a rendered table
More docs here: https://sqlite-utils.datasette.io/en/stable/cli.html#queryin...It's even in an non-intuitive place in the documentation.