9

I know I can use wc to return the total number of words (and lines) in a file using:

wc <filename>

Is there a way to return the count for a specific string on a specific line of a file like the following:

wc <filename> -<flag> <line number> -<flag> <string> 

5 Answers 5

12

This needs to be done in three steps:

  1. Select line number N (example uses line 42):

    sed '42!d'
    
  2. Search the line for all occurrences of a specific pattern (here the string/regular expression hello) and print those separately:

    grep -o 'hello'
    
  3. Count the matches:

    wc -l
    

Or to put it in one single command pipe, reading from file.txt:

sed '42!d' file.txt | grep -o 'hello' | wc -l
6
  • 1
    grep has a count mode, the wc is not needed
    – hildred
    Jun 30, 2017 at 0:30
  • 2
    @hildred grep's -c counts matching lines - not much use to get the number of matching strings in a single line Jun 30, 2017 at 1:41
  • Actually if you split that line with sed into multiple lines then grep -c can be used with no problems Jun 30, 2017 at 17:06
  • @SergiyKolodyazhnyy but then you'd be going out of your way to make something contrived. Unix philosophy is for programs to be focused and composable. grep is for searching, and wc is for counting. Composing them is more natural, and there is nothing wrong with that.
    – JoL
    Jun 30, 2017 at 19:28
  • @jlmg there's nothing particularly contrived about such approach. Taking out a line and splitting it into multiple lines doesn't go against Unix philosophy in any way. sed does its job, grep does its job, and we merely string them together. See my asnwer, I added this as a solution Jun 30, 2017 at 19:38
8

This is a good use case for putting Unix tools together in a pipeline.

line=5
str="ipsum"
sed -n "${line}p" filename | grep -o -- "$str" | wc -l

The sed p command outputs the given line of the file, and feeds it into grep. Grep's -o option tells it to output all the matches for the given string, and each match is output on a separate line. Grep's output is fed to wc, which counts the number of lines.

7

Python

Here's one way to do it in Python via list comprehension (see below for alternative shorter version).

$ python -c 'import sys;print([ l for i,l in enumerate(sys.stdin,1) if i==2][0].count("word"))' < input.txt                                          
3
$ cat input.txt
nothing here
word and another word, and one more word
last line

How this works:

  • we run python interpreter with -c flag, were commands are contained within single quotes;
  • the input file input.txt is redirected into stdin stream of the python interpreter via < shell operator. Hence we need sys module.
  • Using list comprehension structure [something for item in something], we read lines of text from sys.stdin.
  • enumerate(sys.stdin,1) allows us to count enumerate the lines, i.e. with each iteration of list comprehension, we'll get the line of text into l variable and index into i variable starting the count at 1.
  • The i==2 will filter out only line which index equals to 2. That's how we know which line to extract.
  • Thus as a result our list will contain only one item, and within the list its index is 0. So, we refer to that item as [<list comprehension stuff here>][0]. -The .count("word") is what actually does the job of counting. By definition it returns a number of non-overlapping occurrences of a substring in a string.
  • finally all of that stuff was contained within print() statement. So whatever number the .count() method returns will show up on screen.

Shorter version

The shorter way to do the same in Python would be to use readlines() method instead of list comprehension, and refer to specific item in the list that readlines() produces. Note, that readlines() produces a list, and lists in Python are 0-indexed, which means if you want to read line x, you should reference list item x-1. For instance,

$ python -c 'import sys;print(sys.stdin.readlines()[1].count("word"))' < input.txt       
3

sed+grep

Of course, we don't have to stick with scripting languages alone. sed and grep provide sufficient tools which we can use to suit our needs. With grep -c we can count occurrence of matched lines, so all we have to do is extract the specific line we need, and split all words in that line into separate lines. Like so:

$ sed -n  '2{s/ /\n/g;p}' input.txt | grep -c 'word'
3
8
  • Why do you read from sys.stdin instead of just opening the file in Python using open("input.txt")?
    – Byte Commander
    Jun 30, 2017 at 6:34
  • @ByteCommander no particular reason. That can be done,too. Although open() makes the line a little longer. There's no particular advantage either way,though Jun 30, 2017 at 6:37
  • 2
    python -c 'n,w,f=2,"word",open("input.txt");[f.readline()for _ in range(n-1)];print(f.readline().count(w))' would be my personal suggestion.
    – Byte Commander
    Jun 30, 2017 at 6:45
  • @ByteCommander to your first comment: Probably because not calling the close method on the result of open is a memory leak?
    – cat
    Jun 30, 2017 at 16:52
  • 1
    @jlmg the Python one will work with spaces, sed + grep one won't, agreed there Jun 30, 2017 at 20:15
6

awk solution:

awk 'NR==X { print gsub("word",""); }' file
  • change the X with your specific line number.
  • change the "word" with your desired word.
  • gsub returns number of replacement of the "word", looks like we are counting it.

Exampe:

$ cat file:
a b c a a d
d e f f f 1

let's see how many 'f' we got in line "2":

$ awk 'NR==2 { print gsub("f",""); }' file
3
5

One way to do it, in perl:

perl -lne '
  BEGIN{($lineno, $str) = splice @ARGV,0,2} 
  print $c = () = /$str/g if $. == $lineno
' <lineno> <string> <filename>

You must log in to answer this question.

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