What's really tripping me about this is the quote marks.

I have a file.txt with lines like:

{"a":"town, state, country","e":["john@company.com"],"n":"john smith"}
{"a":"town, state, country","e":["zac@company.com","zacsurname@gmail.com"],"n":"zac surname"}
{"a":"town, state, country","n":"jane doe"}

and I'm looking only for first names and emails together, and discarding data that doesn't have both. So the output.txt for the 3 lines above should be:

john john@company.com
zac zac@company.com
zac zacsurname@gmail.com

I tried awk, nawk, pcregrep, sed, perl, eg:

awk -F ":" '$1 ~ /^e/ && $1 ~ /^n/ { print $1,$1 }' file.txt > output.txt
awk -F "\"e\":\"" '{ print $1}' file.txt > output.txt
nawk '/\"e\":[\"/, /\"]/' file.txt > output.txt
pcregrep -o '(?<=[\").*?(?=\"])' <<< file.txt > output.txt

none of it works. Thank you for your help.

  • @bndotivbneiok all relevant information, including your attempt, needs to be in your question, not spread out in comments where it can be missed. I copied your code into your question for you this time. Also, going forward never just say "none of it works" - that's the worst possible problem description as it doesn't tell us anything useful that we could use to help you. Always say in what way it "doesn't work", e.g. error messages, wrong output, no output, etc.
    – Ed Morton
    Jul 22 at 13:08

Since your file is a JSON document, it makes most sense to use a JSON parser, such as jq, to parse it:

jq -r '
    select(has("n") and has("e")) |
    (.n|split(" ")[0]) as $name |
    .e[] | [ $name, . ] | @tsv' file.txt

This selects all objects from your set of objects that has both a n and an e key and discards the rest.

For each selected object, the value of the n key is split on spaces and the first generated word is assigned to the internal variable $name.

We then iterate over the elements of the e array and create arrays with the $name value and the element (an email address). Each of these arrays is given to the @tsv operator, which outputs the data as a tab-delimited list of two fields.

The result will be

john    john@company.com
zac     zac@company.com
zac     zacsurname@gmail.com

Note to interested readers: I extended this to extract only those entries with 1st names matching a list of allowed names in a file. See my answer to Grep first word of a line from a file

  • It looks like it should be easy to parse text-based formats such as JSON, CSV, or XML/HTML manually — but all those formats have complications and corner cases and surprises, and none of us has the time needed to write a full, robust parser.  So the best answer is not to!  A good developer knows when to start coding, and when to stand on the shoulders of other people who have already done the hard work!
    – gidds
    Jul 22 at 20:29
  • Upvoted before finishing the first paragraph, because it’s spot on Jul 22 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.