Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: --format tsv for more convenient processing via CLI #89

Open
polarathene opened this issue May 12, 2024 · 2 comments
Open

Comments

@polarathene
Copy link

TL;DR

It'd be great to have a CSV / TSV as a --format type as output for feeding into other tools like jq, yq, or anything else that can operate on CSV / TSV formatted data (csview, qsv, csvlens).

Justification

--format column is quite close to this, but you can't just delimit on space (MX record with preference, TXT, etc). Whereas TSV uses tabs to delimit, TXT would not need to be quote wrapped when using --txtconcat for example (when TXT records are longer than 255 chars).

Below I've requested configurable columns but TSV may be simpler (yq is a Go project with CSV and TSV output if it helps as reference?), where column order and other manipulations (deleting columns) can be performed by an external tool.

The current json + yaml outputs are more complicated in layout than doggo for this type of processing (although I'm sure it's useful for programmatic access). See the below yq example required to manipulate the records just to exclude TTL for a custom format and removal of record duplicates due to a bug with q when CNAME records are queried with other types. A TSV output that is more aligned with --format types pretty and column would greatly simplify that sort of processing.

It'd effectively make --format column redundant (but less convenient), presumably the request below for configurable columns to display in this mode wouldn't be a big addition, but I think TSV output is a great value add by itself? (eg: Markdown and CLI table output via piping into csview)

Feature Request

From my original feedback: #72 (comment)

Column Headers

Not too important, and definitely should be optional (opt-in is fine), this seems to compliment the column alignment feature as shown in the doggo example above.

Likewise, I don't think you can filter columns? (eg: If I'm interested in the equivalent output of q --short, but additionally with the record type).

NOTE: doggo doesn't appear to support disabling the headers line, or columns displayed. This is just a feature I've seen in some other CLI tools when presenting results.

In a follow-up comment I share a way to parse the JSON/YAML output from q --format yaml for the desired display format:

TYPE    DATA
NS      ns1.example.test.
MX      10 mail.example.test.
TXT     v=spf1 mx -all
A       172.16.42.11

q doesn't have column headers like doggo, so this request is just for the ability to toggle off the TTL column (+nottls / --no-ttls?). There's presently --pretty-ttls and --short-ttls, but no opt-out option.

Similarly since the NAME column is all the same in the queries I've been doing, opt-out for that could also be useful? (EDIT: I've noticed this is not the case when resolving CNAME records where the context may be useful, --format column lacks this too).

So instead of support for a +nottls / --no-ttls, it could instead be an option like --hide-fields / +nofields / --hide-columns (headers is another valid term?) with a list of fields/columns to hide name,ttl? That'd allow for the inverse --fields type,answer, or --headers type,answer / +noheaders name,ttl. You could use the order in the list for column order too :)


NOTE: Technically this could also be a different output type like CSV or TSV that both jq and yq support as output and input formats. Then filtering out the columns manually is much simpler and q doesn't have to worry about display (see the github.com example with large TXT value, which doesn't wrap into column by considering terminal width)

@polarathene
Copy link
Author

Manually processing q output:

Since the original referenced issue, the JSON/YAML output layout has changed; thus the format transform commands I shared previously to perform this feature request manually are now broken.

Here's the updated version 🎉 :

Rewrite v1
q example.test --format yaml \
    | yq '.[].replies[].answer[]
        | [with_entries(select(.key != "hdr"))]
        | with(select(.[] | has("mx")); .[] |= (.mx = ([.preference, .mx] | join(" "))) |= del .preference)
        | with(select(.[] | has("txt")); .[].txt |= join(""))
        | with(select(.[] | has("target")); .[].cname = .[].target | del(.[].target))' \
    | yq 'sort_by(.[] | .) | (.[] | to_entries | with(.[]; . |= [.key | upcase, .value]))' \
    | yq -o=tsv '[["TYPE", "DATA"]] + .' | uniq

Breakdown of the command:

  1. Take the YAML output from q
  2. Take answer(s) from the replies array.
  3. Filter out any fields with answer.hdr, we're only interested in the other field which is the record type as the key. Unlike doggo there is no fixed field name to get the record type (there is however for an integer representation, but then you need to map that to the string/pretty name)
  4. MX records have a separate preference and address (mx key), join these two together and delete the preference key afterwards.
  5. TXT is an array of values since these can be multiple pieces for larger records, merge them all into a single value, quote wrapping is dropped.
  6. CNAME instead has the key target, reassign to cname key and delete the target key to correct this.
  7. 2nd yq command: Sort the list of key: value items, this will be used to remove duplicates later.
  8. Convert the list from key: value to [key, value] items and uppercase the key name for output display.
  9. 3rd yq command: Convert and output as TSV with TYPE + VALUE as the column headers
  10. Finally since output is already sorted, remove any duplicates with uniq as q lacks this capability currently (at least when CNAME records exist).

The multiple yq commands are required. Presumably because during the processing the data is no longer handled a single YAML document, only after finalizing to output. So transforms are applied on the data, and when needed to operate on the whole document again, piped to a new yq command.

Several output examples (Click to expand)

Example for google.com:

TYPE    DATA
MX      10 smtp.google.com.
A       142.251.221.78
AAAA    2404:6800:4006:80a::200e
TXT     MS=E4A68B9AB2BB9670BCE15412F62916164C0B20BB
TXT     apple-domain-verification=30afIBcvSuDV2PLX
TXT     docusign=05958488-4752-4ef2-95eb-aa7ba8a3bd0e
TXT     docusign=1b0a6754-49b1-4db5-8540-d2c12664b289
TXT     facebook-domain-verification=22rm551cu4k0ab0bxsw536tlds4h95
TXT     globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8=
TXT     google-site-verification=TV9-DBe4R80X4v0M4U_bd_J9cpOJM0nikft0jAgjmsQ
TXT     google-site-verification=wD8N7i1JTNTkezJ49swvWW48f8_9xveREV4oB-0Hf5o
NS      ns1.google.com.
NS      ns2.google.com.
NS      ns3.google.com.
NS      ns4.google.com.
TXT     onetrust-domain-verification=de01ed21f2fa4d8781cbc3ffb89cf4ef
TXT     v=spf1 include:_spf.google.com ~all
TXT     webexdomainverification.8YX6G=6e6922db-e3e6-4a36-904e-a805c28087fa

Example for github.com (has a TXT with multiple values, some DNS don't handle this, hence @1.1.1.1 may be needed to avoid error):

TYPE    DATA
MX      1 aspmx.l.google.com.
MX      10 alt3.aspmx.l.google.com.
MX      10 alt4.aspmx.l.google.com.
TXT     1dx40j0v3l3cnnhd973dfvvrm6z1bjk5
A       20.248.137.48
MX      5 alt1.aspmx.l.google.com.
MX      5 alt2.aspmx.l.google.com.
TXT     MS=6BF03E6AF5CB689E315FB6199603BABF2C88D805
TXT     MS=ms44452932
TXT     MS=ms58704441
TXT     adobe-idp-site-verification=b92c9e999aef825edc36e0a3d847d2dbad5b2fc0e05c79ddd7a16139b48ecf4b
TXT     apple-domain-verification=RyQhdzTl6Z6x8ZP4
TXT     atlassian-domain-verification=jjgw98AKv2aeoYFxiL/VFaoyPkn3undEssTRuMg6C/3Fp/iqhkV4HVV7WjYlVeF8
TXT     beautifulai-site-verification=e478d764-9335-4af3-ac7a-2d5ab61b59aa
TXT     calendly-site-verification=at0DQARi7IZvJtXQAWhMqpmIzpvoBNF7aam5VKKxP
NS      dns1.p08.nsone.net.
NS      dns2.p08.nsone.net.
NS      dns3.p08.nsone.net.
NS      dns4.p08.nsone.net.
TXT     docusign=087098e3-3d46-47b7-9b4e-8a23028154cd
TXT     facebook-domain-verification=39xu4jzl7roi7x0n93ldkxjiaarx50
TXT     google-site-verification=UTM-3akMgubp6tQtgEuAkYNYLyYAvpTnnSrDMWoDR3o
TXT     krisp-domain-verification=ZlyiK7XLhnaoUQb2hpak1PLY7dFkl1WE
TXT     loom-site-verification=f3787154f1154b7880e720a511ea664d
TXT     miro-verification=d2e174fdb00c71e0bcf58f8e58c3da2dd80dcfa9
NS      ns-1283.awsdns-32.org.
NS      ns-1707.awsdns-21.co.uk.
NS      ns-421.awsdns-52.com.
NS      ns-520.awsdns-01.net.
TXT     stripe-verification=f88ef17321660a01bab1660454192e014defa29ba7b8de9633c69d6b4912217f
TXT     v=spf1 ip4:192.30.252.0/22 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com include:spf.protection.outlook.com include:mail.zendesk.com include:_spf.salesforce.com include:servers.mcsv.net ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ip4:167.89.101.2 ip4:167.89.101.192/28 ip4:192.254.112.60 ip4:192.254.112.98/31 ip4:192.254.113.10 ip4:192.254.113.101 ip4:192.254.114.176 ip4:62.253.227.114 ~all

vs:

$ q @1.1.1.1 github.com --txtconcat --round-ttls

github.com. 7m NS dns1.p08.nsone.net.
github.com. 7m NS dns2.p08.nsone.net.
github.com. 7m NS dns3.p08.nsone.net.
github.com. 7m NS dns4.p08.nsone.net.
github.com. 7m NS ns-1283.awsdns-32.org.
github.com. 7m NS ns-1707.awsdns-21.co.uk.
github.com. 7m NS ns-421.awsdns-52.com.
github.com. 7m NS ns-520.awsdns-01.net.
github.com. 54m MX 1 aspmx.l.google.com.
github.com. 54m MX 10 alt3.aspmx.l.google.com.
github.com. 54m MX 10 alt4.aspmx.l.google.com.
github.com. 54m MX 5 alt1.aspmx.l.google.com.
github.com. 54m MX 5 alt2.aspmx.l.google.com.
github.com. 54m TXT "1dx40j0v3l3cnnhd973dfvvrm6z1bjk5"
github.com. 54m TXT "MS=6BF03E6AF5CB689E315FB6199603BABF2C88D805"
github.com. 54m TXT "MS=ms44452932"
github.com. 54m TXT "MS=ms58704441"
github.com. 54m TXT "adobe-idp-site-verification=b92c9e999aef825edc36e0a3d847d2dbad5b2fc0e05c79ddd7a16139b48ecf4b"
github.com. 54m TXT "apple-domain-verification=RyQhdzTl6Z6x8ZP4"
github.com. 54m TXT "atlassian-domain-verification=jjgw98AKv2aeoYFxiL/VFaoyPkn3undEssTRuMg6C/3Fp/iqhkV4HVV7WjYlVeF8"
github.com. 54m TXT "beautifulai-site-verification=e478d764-9335-4af3-ac7a-2d5ab61b59aa"
github.com. 54m TXT "calendly-site-verification=at0DQARi7IZvJtXQAWhMqpmIzpvoBNF7aam5VKKxP"
github.com. 54m TXT "docusign=087098e3-3d46-47b7-9b4e-8a23028154cd"
github.com. 54m TXT "facebook-domain-verification=39xu4jzl7roi7x0n93ldkxjiaarx50"
github.com. 54m TXT "google-site-verification=UTM-3akMgubp6tQtgEuAkYNYLyYAvpTnnSrDMWoDR3o"
github.com. 54m TXT "krisp-domain-verification=ZlyiK7XLhnaoUQb2hpak1PLY7dFkl1WE"
github.com. 54m TXT "loom-site-verification=f3787154f1154b7880e720a511ea664d"
github.com. 54m TXT "miro-verification=d2e174fdb00c71e0bcf58f8e58c3da2dd80dcfa9"
github.com. 54m TXT "stripe-verification=f88ef17321660a01bab1660454192e014defa29ba7b8de9633c69d6b4912217f"
github.com. 54m TXT "v=spf1 ip4:192.30.252.0/22 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com include:spf.protection.outlook.com include:mail.zendesk.com include:_spf.salesforce.com include:servers.mcsv.net ip4:166.78.69.169 ip4:166.78.69.170 ip4:166.78.71.131 ip4:167.89.101.2 ip4:167.89.101.192/28 ip4:192.254.112.60 ip4:192.254.112.98/31 ip4:192.254.113.10 ip4:192.254.113.101 ip4:192.254.114.176 ip4:62.253.227.114 ~all"
github.com. 0s A 20.248.137.48

Example for static.crates.io (has a TARGET => CNAME record, duplicates filtered out)

TYPE    DATA
A       151.101.130.137
A       151.101.194.137
A       151.101.2.137
A       151.101.66.137
AAAA    2a04:4e42:200::649
AAAA    2a04:4e42:400::649
AAAA    2a04:4e42:600::649
AAAA    2a04:4e42::649
CNAME   dualstack.k.sni.global.fastly.net.
CNAME   fastly-static.crates.io.

Rewrite v2

Got a bit more familiar with yq after the first rewrite. Now it uses a single yq call and can better support multiple columns than the prior version.

q @1.1.1.1 static.crates.io --format yaml | yq -o tsv '.[].replies[].answer[]
  |
    {"name": .hdr.name} * (
    omit(["hdr"])
      | with(select(. | has("mx")); . = { "mx": ([.preference, .mx] | join(" ")) })
      | to_entries | { "type": .[].key, "data": .[].value }
    )
  | (.
    | with(select(.type == "target"); .type = "cname")
    | with(select(.type == "txt"); .data |= join(""))
  ) |= [["NAME", "TYPE", "VALUE"]] + [[.name, (.type | upcase), .data]]' \
  | sort -u | csview --tsv --style rounded --header-align left

Breakdown of the command:

  1. Take the YAML output from q
  2. Take answer(s) from the replies array.
  3. Extract and assign the hdr.name value to a new object.
  4. Take the answer item but ignoring the hdr object field (omit):
    • Check for an MX record, if there is one the answers item object has an extra field preference, join into a space delimited string.
    • Take the data for step 4 and convert the non-hdr fields (mx, ns, a, cname, etc) into entries (field name as value for .key, value of field in an adjacent field as .value_) to normalize these from q, renaming .key to type and .value to data.
  5. Steps 3 and 4 are merged (* operator) together into a single new object replacing their respective object from the answer array.
  6. With the normalized type field we can check more easily the record type and make any adjustments (target to cname and concatenate TXT data).
  7. The objects are then converted to arrays with the values in the desired order. type value is uppercased. The column headers are also prepended (uppercase headings ensures higher precedence via sort -u next).
  8. Finally sort the rows and remove duplicates. Output for yq -o tsv ensures it's in TSV and we pipe it through to csview to display nicely 🎉

Examples of output

Terminal output

$ csview --tsv --header-align left --style rounded

╭────────────────────────────────────┬───────┬────────────────────────────────────╮
│ NAME                               │ TYPE  │ VALUE                              │
├────────────────────────────────────┼───────┼────────────────────────────────────┤
│ dualstack.k.sni.global.fastly.net. │ A     │ 151.101.130.137                    │
│ dualstack.k.sni.global.fastly.net. │ A     │ 151.101.194.137                    │
│ dualstack.k.sni.global.fastly.net. │ A     │ 151.101.2.137                      │
│ dualstack.k.sni.global.fastly.net. │ A     │ 151.101.66.137                     │
│ dualstack.k.sni.global.fastly.net. │ AAAA  │ 2a04:4e42:200::649                 │
│ dualstack.k.sni.global.fastly.net. │ AAAA  │ 2a04:4e42:400::649                 │
│ dualstack.k.sni.global.fastly.net. │ AAAA  │ 2a04:4e42:600::649                 │
│ dualstack.k.sni.global.fastly.net. │ AAAA  │ 2a04:4e42::649                     │
│ fastly-static.crates.io.           │ CNAME │ dualstack.k.sni.global.fastly.net. │
│ static.crates.io.                  │ CNAME │ fastly-static.crates.io.           │
╰────────────────────────────────────┴───────┴────────────────────────────────────╯

$ csview --tsv --header-align left --style markdown

| NAME                               | TYPE  | VALUE                              |
|------------------------------------|-------|------------------------------------|
| dualstack.k.sni.global.fastly.net. | A     | 151.101.130.137                    |
| dualstack.k.sni.global.fastly.net. | A     | 151.101.194.137                    |
| dualstack.k.sni.global.fastly.net. | A     | 151.101.2.137                      |
| dualstack.k.sni.global.fastly.net. | A     | 151.101.66.137                     |
| dualstack.k.sni.global.fastly.net. | AAAA  | 2a04:4e42:200::649                 |
| dualstack.k.sni.global.fastly.net. | AAAA  | 2a04:4e42:400::649                 |
| dualstack.k.sni.global.fastly.net. | AAAA  | 2a04:4e42:600::649                 |
| dualstack.k.sni.global.fastly.net. | AAAA  | 2a04:4e42::649                     |
| fastly-static.crates.io.           | CNAME | dualstack.k.sni.global.fastly.net. |
| static.crates.io.                  | CNAME | fastly-static.crates.io.           |

Other examples via qsv:

# Similar to the TSV output from yq that my command outputs,
# except the columns are properly aligned this way (elastic tabstops):
$ qsv fmt -d "\t" | qsv table
NAME                                TYPE   VALUE
dualstack.k.sni.global.fastly.net.  A      151.101.130.137
dualstack.k.sni.global.fastly.net.  A      151.101.194.137
dualstack.k.sni.global.fastly.net.  A      151.101.2.137
dualstack.k.sni.global.fastly.net.  A      151.101.66.137
dualstack.k.sni.global.fastly.net.  AAAA   2a04:4e42:200::649
dualstack.k.sni.global.fastly.net.  AAAA   2a04:4e42:400::649
dualstack.k.sni.global.fastly.net.  AAAA   2a04:4e42:600::649
dualstack.k.sni.global.fastly.net.  AAAA   2a04:4e42::649
fastly-static.crates.io.            CNAME  dualstack.k.sni.global.fastly.net.
static.crates.io.                   CNAME  fastly-static.crates.io.


# TSV to JSONL
$ qsv fmt -d "\t" | qsv tojsonl 2>/dev/null
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"A","VALUE":"151.101.130.137"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"A","VALUE":"151.101.194.137"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"A","VALUE":"151.101.2.137"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"A","VALUE":"151.101.66.137"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"AAAA","VALUE":"2a04:4e42:200::649"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"AAAA","VALUE":"2a04:4e42:400::649"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"AAAA","VALUE":"2a04:4e42:600::649"}
{"NAME":"dualstack.k.sni.global.fastly.net.","TYPE":"AAAA","VALUE":"2a04:4e42::649"}
{"NAME":"fastly-static.crates.io.","TYPE":"CNAME","VALUE":"dualstack.k.sni.global.fastly.net."}
{"NAME":"static.crates.io.","TYPE":"CNAME","VALUE":"fastly-static.crates.io."}


# Easily fix my mistake in this writeup with VALUE column name changed to DATA
# Additionally reorder the columns (can also omit those you don't need)
# Finally render as a nice table via csview (useful as markdown for sharing on Github or automating docs)
$ qsv rename NAME,TYPE,DATA | qsv select TYPE,DATA,NAME | csview
┌───────┬────────────────────────────────────┬────────────────────────────────────┐
│ TYPE  │                DATA                │                NAME                │
├───────┼────────────────────────────────────┼────────────────────────────────────┤
│ A     │ 151.101.130.137                    │ dualstack.k.sni.global.fastly.net. │
│ A     │ 151.101.194.137                    │ dualstack.k.sni.global.fastly.net. │
│ A     │ 151.101.2.137                      │ dualstack.k.sni.global.fastly.net. │
│ A     │ 151.101.66.137                     │ dualstack.k.sni.global.fastly.net. │
│ AAAA  │ 2a04:4e42:200::649                 │ dualstack.k.sni.global.fastly.net. │
│ AAAA  │ 2a04:4e42:400::649                 │ dualstack.k.sni.global.fastly.net. │
│ AAAA  │ 2a04:4e42:600::649                 │ dualstack.k.sni.global.fastly.net. │
│ AAAA  │ 2a04:4e42::649                     │ dualstack.k.sni.global.fastly.net. │
│ CNAME │ dualstack.k.sni.global.fastly.net. │ fastly-static.crates.io.           │
│ CNAME │ fastly-static.crates.io.           │ static.crates.io.                  │
└───────┴────────────────────────────────────┴────────────────────────────────────┘

Rendered

Github text above doesn't look as nice as it does on the actual terminal.

image

NAME TYPE VALUE
dualstack.k.sni.global.fastly.net. A 151.101.130.137
dualstack.k.sni.global.fastly.net. A 151.101.194.137
dualstack.k.sni.global.fastly.net. A 151.101.2.137
dualstack.k.sni.global.fastly.net. A 151.101.66.137
dualstack.k.sni.global.fastly.net. AAAA 2a04:4e42:200::649
dualstack.k.sni.global.fastly.net. AAAA 2a04:4e42:400::649
dualstack.k.sni.global.fastly.net. AAAA 2a04:4e42:600::649
dualstack.k.sni.global.fastly.net. AAAA 2a04:4e42::649
fastly-static.crates.io. CNAME dualstack.k.sni.global.fastly.net.
static.crates.io. CNAME fastly-static.crates.io.

Reference (comparison of accomplishing the same with doggo JSON output)

As per the original issue of mine, this is the equivalent yq (or jq) for processing the --json output option of doggo - which is far simpler to process.

If q were to alter the --format json / --format yaml output, it should probably be via a separate modifier option to avoid breaking any reliance on the current structure which has much more information available.

# NOTE: The sub operator is only to remove escaped double quote wrapping of TXT values (due to JSON).
# Not doing so will make TXT values inconsistent with the TSV output when processed between these tools.

# jq
doggo example.test NS MX TXT A --json | jq -r '["TYPE", "DATA"], (.[] | [.answers[0].type, .answers[0].address])
  | .[1] |= sub("\""; ""; "g")
  | @tsv'

# yq
doggo example.test NS MX TXT A --json \
  | yq -o=tsv '[["TYPE", "DATA"]] + [.[]
    | [.answers[0].type, .answers[0].address]
    | .[1] |= sub("\"", "")
    ]'

@polarathene
Copy link
Author

polarathene commented May 12, 2024

One last example with a reference input from a standard q command with duplicate entries (due to TTL?).

# --Use this q output for processing in next codefence--
# x3 static.crates.io => fastly-static.crates.io
# x4 fastly-static.crates.io => dualstack.k.sni.global.fastly.net
$ q @1.1.1.1 CNAME A NS TXT AAAA static.crates.io

static.crates.io. 5m CNAME fastly-static.crates.io.
dualstack.k.sni.global.fastly.net. 30s A 151.101.130.137
dualstack.k.sni.global.fastly.net. 30s A 151.101.194.137
dualstack.k.sni.global.fastly.net. 30s A 151.101.2.137
dualstack.k.sni.global.fastly.net. 30s A 151.101.66.137
fastly-static.crates.io. 24s CNAME dualstack.k.sni.global.fastly.net.
static.crates.io. 4m24s CNAME fastly-static.crates.io.
fastly-static.crates.io. 10s CNAME dualstack.k.sni.global.fastly.net.
static.crates.io. 4m10s CNAME fastly-static.crates.io.
dualstack.k.sni.global.fastly.net. 6s AAAA 2a04:4e42:200::649
dualstack.k.sni.global.fastly.net. 6s AAAA 2a04:4e42:400::649
dualstack.k.sni.global.fastly.net. 6s AAAA 2a04:4e42:600::649
dualstack.k.sni.global.fastly.net. 6s AAAA 2a04:4e42::649
fastly-static.crates.io. 36s CNAME dualstack.k.sni.global.fastly.net.
static.crates.io. 4m36s CNAME fastly-static.crates.io.
# Pipe into qsv with space as the delimiter,
# which is ok for this example but would break with other records
# `rename --no-headers` is used to prepend headers
# Although you could avoid adding headers and just rearrange/hide columns via select indices
$ qsv fmt -d " " \
| qsv rename --no-headers NAME,TTL,TYPE,DATA \
| qsv select TYPE,NAME,DATA \
| qsv dedup --quiet \
| csview --header-align left --style markdown

| TYPE  | NAME                               | DATA                               |
|-------|------------------------------------|------------------------------------|
| A     | dualstack.k.sni.global.fastly.net. | 151.101.130.137                    |
| A     | dualstack.k.sni.global.fastly.net. | 151.101.194.137                    |
| A     | dualstack.k.sni.global.fastly.net. | 151.101.2.137                      |
| A     | dualstack.k.sni.global.fastly.net. | 151.101.66.137                     |
| AAAA  | dualstack.k.sni.global.fastly.net. | 2a04:4e42:200::649                 |
| AAAA  | dualstack.k.sni.global.fastly.net. | 2a04:4e42:400::649                 |
| AAAA  | dualstack.k.sni.global.fastly.net. | 2a04:4e42:600::649                 |
| AAAA  | dualstack.k.sni.global.fastly.net. | 2a04:4e42::649                     |
| CNAME | fastly-static.crates.io.           | dualstack.k.sni.global.fastly.net. |
| CNAME | static.crates.io.                  | fastly-static.crates.io.           |

No duplicates 👍

TYPE NAME DATA
A dualstack.k.sni.global.fastly.net. 151.101.130.137
A dualstack.k.sni.global.fastly.net. 151.101.194.137
A dualstack.k.sni.global.fastly.net. 151.101.2.137
A dualstack.k.sni.global.fastly.net. 151.101.66.137
AAAA dualstack.k.sni.global.fastly.net. 2a04:4e42:200::649
AAAA dualstack.k.sni.global.fastly.net. 2a04:4e42:400::649
AAAA dualstack.k.sni.global.fastly.net. 2a04:4e42:600::649
AAAA dualstack.k.sni.global.fastly.net. 2a04:4e42::649
CNAME fastly-static.crates.io. dualstack.k.sni.global.fastly.net.
CNAME static.crates.io. fastly-static.crates.io.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant