7

I'm trying to write a grep command to find lines like the below in a large text file:

<div class="node_thumbnail" data-type="file" name="GOPR0036.MP4_frame000001.jpg" data="813334c25191468c9f1c57afc99fde60" aid="133948" rel="/Files/ToolTipView?fileId=813334c25191468c9f1c57afc99fde60&pageNo=1&NoCache=101016083044" rev="topMiddle">

but the + symbol seems to be causing problems in the below commands:

 grep 'data=[a-z,0-9,\"]' file

Lots of hits

 grep 'data=[a-z,0-9,\"]+' file

No hits

3
  • 1
    A large HTML file, to be more specific ... not that there's anything wrong with that ... Oct 11, 2016 at 10:35
  • just don't forget to take the habit of having: LC_ALL="C" grep ... instead of grep ... , so that [a-z] means always ascii letters a to z (ie, all lowercase ascii letters) instead of funny alternatives (ex: every letters except "Z", in some locales...) Oct 11, 2016 at 11:28
  • and [a-z,0-9,\"] contains 2 occurence of ",", which is not necessary. you can have the same effect with [a-z,0-9\"] Oct 11, 2016 at 11:30

3 Answers 3

14

If you want + to mean "one or more of the preceding atom", then you have to do one of:

  1. Use -E (Extended Regular Expressions) (or -P, PCRE):

    grep -E 'data=[a-z,0-9,\"]+' file
    
  2. Escape + so that is treated specially in the Basic Regular Expressions used by default in grep:

    grep 'data=[a-z,0-9,"]\+' file
    
3
  • @MartinKS by the way, if all you want is the quoted string after data=, you could just run grep -P 'data=".+?" or, to get only the string and nothing else: grep -oP '\bdata="\K[^"]+'.
    – terdon
    Oct 10, 2016 at 12:52
  • 2
    @MartinKS whoops, to get only the string, you need the -o option as well. I edited my previous comment. And you're welcome :)
    – terdon
    Oct 10, 2016 at 12:58
  • @terdon, or just sed -n 's/.*data="\([^"][^"]*\)".*/\1/p' file, which is fully POSIX compliant. (And sed -nE 's/.*data="([^"]+)".*/\1/p' file will be POSIX compliant for the next edition of POSIX.)
    – Wildcard
    Oct 11, 2016 at 10:42
9

Points:

  • + is an ERE (Extended Regular Expression) token, which indicates one or more of the preceding token, can be used if -E option of grep is used or with escaped (\+) in case of BRE (Basic Regex) i.e. only regular grep

  • The character class [a-z,0-9,\"] would match any of the characters between [a-z], [0-9], , or ". This may not be what you want

  • Normally grep outputs whole line, if you want to output only the matched portion, use -o option of grep


Based on your example, you can do:

grep -E '\bdata=[a-z0-9"]+\b' file
  • -E enables ERE
  • \b matches string edges, zero width
  • data= matches data= literally
  • [a-z0-9"] matches any character of [a-z], [0-9], and ". + matches the previous token one or more times

Your current pattern even you make it correct, without \b this would match false positives like foo fdata=2322ab, data=12AB and so on.

Example:

% grep -oE '\bdata=[a-z0-9"]+\b' <<<'<div class="node_thumbnail" data-type="file" name="GOPR0036.MP4_frame000001.jpg" data="813334c25191468c9f1c57afc99fde60" aid="133948" rel="/Files/ToolTipView?fileId=813334c25191468c9f1c57afc99fde60&pageNo=1&NoCache=101016083044" rev="topMiddle"'
data="813334c25191468c9f1c57afc99fde60
-1

Another option is to use egrep:

egrep 'data=[a-z,0-9,\"]+' file

egrep is bundled with grep, it is just a wrapper for grep:

#!/bin/sh
exec grep -E "$@"

this is good for interactive use. However in scripts I would use grep -E.

2
  • Please don't recommend using egrep. It has been deprecated in favor of grep -E for many years now. As the POSIX specs state, it is still supported and likely to be so for a while but the current POSIX (and GNU) grep has been designed to replace the older egrep and fgrep variants so it is better to use grep -E and grep -f instead.
    – terdon
    Oct 11, 2016 at 11:49
  • egrep is fine for interactive use
    – Zombo
    Oct 11, 2016 at 12:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .