0

I want to count the number of addresses attached to each device in the below example. Preferably using awk or sed.

/dev/1
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
/dev/2
wwpn1 sometext sometext sometext
wwpn2 sometext sometext sometext
/dev/3
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
addr4 sometext sometext sometext
/dev/4
addr1 sometext sometext sometext

Below will give me a single match, but I am having trouble incrementing this for all devices outside of just running the command manually a bunch of times:

sed -n '/\/dev\/1/,/\/dev\/2/'p 
/dev/1 
addr1 sometext sometext sometext
addr2 sometext sometext sometext
addr3 sometext sometext sometext
/dev/2

What I am looking to do would iterate through all devices, so something like:

for i in `grep 'dev' somefile`; do sed -n '/$i/,/$insersecondmatchhere/'p ; done

The problem, is that I am not sure how to get the $insertsecondmatchhere variable from the for loop at the beginning of the command.

  • 1
    This question was flagged as "unclear what you're asking". Please edit your post and add an example of desired output. – don_crissti Feb 23 '16 at 15:14
2

Here's a simplistic way in awk; if it sees the /dev/[0-9] pattern, it dumps any existing saved pattern & count, then resets the device & count; otherwise, it increments a counter. Once it reaches EOF, it dumps the saved pattern & count.

#!/bin/awk -f
{
  if ( /\/dev\/[0-9]/ ) {
    if (dev) { print dev, count; };
    dev=$0;
    count=0;
  } else {
    ++count;
  }
}
END {
  print dev, count;
}

and since you have several awk versions and terdon's perl solution, here's an ugly bash + grep + sed solution, since that seemed to be the direction you were going initially:

#!/usr/bin/env bash

declare -a devs
devs=( $(grep ^/dev/ input) )
for ((i=0; i < ${#devs[@]} - 1; i++)); do
  start=${devs[i]}
  end=${devs[i+1]}
  start=${start//\//\\\/}
  end=${end//\//\\\/}
  count=$(sed -n "/^$start/,/^$end/p" input | wc -l)
  count=$(( count - 2 ))
  echo for ${devs[i]}, count is $count
done
start=${devs[i]}
start=${start//\//\\\/}
count=$(sed -n "/^$start/,\$p" input | wc -l)
count=$(( count - 1 ))
echo for ${devs[i]}, count is $count

The main hack is escaping the forward-slashes in the device names before passing them to sed.

1

Something like this?

awk -v RS="/dev/" 'NR!=1 {print "/dev/"$1":"NF-1}' file.txt
0

Another awk approach:

$ awk '{if(/^\//){n=$0;}else{l[n]++}}END{for(n in l){print n" : "l[n]}}' file 
/dev/1 : 3
/dev/2 : 2
/dev/3 : 4
/dev/4 : 1

The awk script will set n to the current line if the current line starts with a /. If it doesn't, it will increase the value of l[n], i.e. the value stored in the associative array l for the current value of n. This is what will count the lines per section. Once the whole file has been read, in the END{} block, we print each n (section name, the dev lines) and the number of lines in that section.

You could golf that to:

awk '{/^\//?n=$0:l[n]++;}END{for(n in l){print n" : "l[n]}}' file 

And the same idea in (golfed) Perl:

$ perl -lne'/^\//?$n=$_:$l{$n}++;}{print"$_ : $l{$_}"for keys(%l)' file 
/dev/2 : 2
/dev/1 : 3
/dev/3 : 4
/dev/4 : 1

Which can be expanded to:

perl -lne 'if(/^\//){$n=$_}
           else{ $l{$n}++ } 
           END{print"$_ : $l{$_}" for keys(%l)}' file 

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.