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?

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 .

  • 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 '14 at 10:45
  • 2
    Probably not tagged regex because the regex is the easy part. – Tom Zych Jun 16 '14 at 10:51
  • Is it actually necessary to sort -n? Doesn't it come out in line number order anyway? – Tom Zych Jun 16 '14 at 10:52
  • You are right, sort -n is not necessary. Thanks. – Moebius Jun 16 '14 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 '14 at 12:54

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
  • 4
    Best solution, in my opinion. Could be even more simplified by reducing by one pipe: grep -o "matchingString" <<< $i | wc -l. – Benjamin W. Dec 20 '15 at 5:57
  • 2
    This will be orders of magnitude slower then other options though – Rahul Jun 21 '18 at 16:45

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.

  • 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 '14 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. – Scott Jun 16 '14 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 '14 at 17:53

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

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


$ 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 "[{}]"

awk can get a total count like this:

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

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.