So I have a file.txt like

John john@gmail.com
Mary mary@gmail.com

but I also have

Pickethunter123 timmy@gmail.com
XPC61 roger@gmail.com
GeorgeBushSucks michael@gmail.com

and I have a list of English names names.txt. Is there a way to grep only by the first word of a line against another file?

  • What did you try? Think of the anchor ^.
    – unxnut
    Jul 22 at 12:37
  • 1
    If this is a followup to your previous question, where you had a JSON file, then it would be a lot easier (or at least convenient) to solve this while parsing that JSON file than doing it as a separate step afterwards. Consider asking a question about your overall goal rather than separate questions about each tiny step that you believe that you need to take.
    – Kusalananda
    Jul 22 at 13:04
  • Can you add an example of names.txt and the desired output?
    – AdminBee
    Jul 23 at 11:48

If you want a regular expression to match only at the start of a pattern, you can add the line anchor ^ to each line of your names.txt file. If you don't want to modify the file in place, you can do that on the fly with sed for example:

sed 's/^/^/' names.txt | grep -wf - file.txt

passing the result to grep -f via standard input -.

Alternatively, awk is a good option for delimited data:

awk 'NR==FNR{fn[$1]; next} $1 in fn' names.txt file.txt

Connecting this to your previous question, where you had a JSON document with the contents

{"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"}

To pull out the list of 1st names (the value up to the first space character in each n key's value) together with the email addresses in e values, I proposed that you'd use jq and did

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

(see my previous answer for a brief explanation about this command).

We may modify this to also incorporate reading a list of valid names:

jq -Rs 'rtrimstr("\n") | split("\n") | map(ascii_upcase)' names.txt |
jq -r '
    . as $valid_names |
    inputs | select(has("n") and has("e")) |
    (.n|split(" ")[0]) as $name | select(($name | ascii_upcase) == $valid_names[]) |
    .e[] | [ $name, . ] | @tsv' - file.txt

This calls jq twice. The first invocation is to transform the names.txt list of names (assumed to be one single name per line) into a JSON array of upper-case strings.

For example, if our names.txt file contains


then the first jq invocation would produce the JSON document


The second jq invocation starts by reading from the first one, creating the set $valid_names from the list of upper-case names.

The inputs function is then used to read the JSON objects from file.txt, one by one, and the rest is pretty much like it was in my previous answer to you, only we restrict the data via an extra select to only be those entries with 1st names that matches a name in the $valid_names set.

With the names.txt file as in my example above, and with file.txt as at the top of this answer, the output would be the single line

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

You can transform the file with cut, then pipe that into your grep command:

cut -d' ' -f1 file.txt | grep -F -f names.txt

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