42

I'm trying to get the number of matches (in this case occurrences of { or }) in each line of a .tex file.

I know that the -o flag returns only the match, but it returns each match on a new line, even combined with the -n flag. I don't know of anything I could pipe this through to count the repeats. The -c flag only returns the total number of matches in the entire file - maybe I could pipe one line at a time to grep?

5 Answers 5

45
grep -o -n '[{}]' <filename> | cut -d : -f 1 | uniq -c

The output will be something like:

3 1
1 2

Meaning 3 occurrences in the first line and 1 in the second.

Taken from https://stackoverflow.com/a/15366097/3378354 .

7
  • Thanks - google found lots of regex hits on SU, but not that one on SO, which doesn't even seem to have a regex tag. The sort isn't strictly necessary as grep's output is sorted by line number, but I guess it's good practice before uniq.
    – Chris H
    Jun 16, 2014 at 10:45
  • 2
    Probably not tagged regex because the regex is the easy part.
    – Tom Zych
    Jun 16, 2014 at 10:51
  • Is it actually necessary to sort -n? Doesn't it come out in line number order anyway?
    – Tom Zych
    Jun 16, 2014 at 10:52
  • You are right, sort -n is not necessary. Thanks.
    – Moebius
    Jun 16, 2014 at 10:58
  • @TomZych, it turned out you were right, but had I known that I might not have asked. The mental jump from grep to tag:regex was perhaps a bit too much though.
    – Chris H
    Jun 16, 2014 at 12:54
4

After reading various solutions, I think this is the easiest approach to the problem:

while read i; do echo $i |grep -o "matchingString"| wc -l;  done < input.txt
2
  • 4
    Best solution, in my opinion. Could be even more simplified by reducing by one pipe: grep -o "matchingString" <<< $i | wc -l. Dec 20, 2015 at 5:57
  • 2
    This will be orders of magnitude slower then other options though
    – Rahul
    Jun 21, 2018 at 16:45
2

Is using grep a requirement?  Here’s an alternative:

sed 's/[^{}]//g' your_file | awk '{print NR, length }'

The sed strips out all characters other than { and } (i.e., leaving only { and } characters), and then the awk counts the characters on each line (which are just the { and } characters).  To suppress lines with no matches,

sed 's/[^{}]//g' your_file | awk '/./ {print NR, length }'

Note that my solution assumes (requires) that the strings you are looking for are single characters.  Moebius’s answer is more easily adapted to multi-character strings.  Also, neither of our answers excludes quoted or escaped occurrences of the characters/strings of interest; e.g.,

{ "nullfunc() {}" }

would be considered to contain four brace characters.

3
  • grep wasn't really a requirement, it was just where I started looking for a solution, because it gave me something close. I've never had a need for awk, so had I not used the answer above I'd have used this as a chance to experiment -- I may still. What I failed to make clear (but it doesn't affect either answer) is that I wanted to run the script once per bracket, to help me track down a mismatch (in LaTeX source, here for a table) where most pairs occur in a single line.
    – Chris H
    Jun 16, 2014 at 15:46
  • I’m not quite sure what you mean by “run the script once per bracket,” but if you want to track down a brace mismatch, you might want to try something like sed 's/{[^{}]*}//g' your_file | grep –n '[{}]', where the sed strips out (matched) pairs. If you have nested pairs, use sed 's/{[^{}]*}//g;s/{[^{}]*}//g;s/{[^{}]*}//g;…' …, repeating the s/{[^{}]*}//g as many times as your deepest nesting. Jun 16, 2014 at 16:41
  • I meant execute `sed 's/[^}]//g' your_file | awk '{print NR, length }' and 's/[^{]//g' your_file | awk '{print NR, length }'. I do indeed have nesting, and working out the deepest level seemed like a chore. Turning many lines into a handful (there are a few cases where the braces only match over multiple lines for valid reasons) worked well (I use jedit which highlights the matching bracket -- for any type of bracket it understands -- so I really did just need to narrow it down).
    – Chris H
    Jun 16, 2014 at 17:53
1

AWK can do this by itself, piping multiple processes is not necessary.

awk '{print NR " " gsub(/[{}]/, "")}'

Example:

$ printf 'test{}\n abc\n }{{}\n xyz}' | awk  '{print NR " " gsub(/[{}]/, "")}'
1 2
2 0
3 4
4 1

The grep count option -c is a con job. It does not return the total count you expect (it is actually just a line count, not a match count).

$ printf 'test{}\n abc\n }{{}\n xyz}' | grep -con "[{}]"
3

awk can get a total count like this:

$ printf 'test{}\n abc\n }{{}\n xyz}' | awk -F"[{}]" '{c+=NF-1} END {print c}'
7
0

Using awk you can search for the occurrence of a string, show the number of times it was found and the linenumber on which it was found. Here I wanted to see only those lines that have the search string more than once. As input I'm using man page for ls. I put the search string (here: 'of') in a variable.

 s='of'; man ls | awk -v s="${s}" '{ if ($0 ~ s) { k = split($0, ar, s); if (k > 2) { print NR, k-1, $0 }}}' | grep "${s}"

Output:

32 2        -c     with -lt: sort by, and show, ctime (time of last modification of file status information); with -l: show ctime and sort by name; otherwise: sort by ctime, newest first
180 2        The  SIZE  argument  is  an integer and optional unit (example: 10K is 10*1024).  Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).  Binary prefixes can be used, too: KiB=K,

The grep statement is used to colorize the search string (not visible here).

You must log in to answer this question.

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