0

There is a file with an unknown number of lines. In the file each line contains unknown many periods (.).

How can I find the maximum period number? I am not interested in finding the line that contains the most periods.

For example: Processing the file content below in bash should give the answer "4".

one.one
two.two.two
three.three.three.three
four..four.
five..five..
six...six
AdminBee
  • 22,803
Bjorn
  • 123
  • 1
  • 6

6 Answers6

3

You could do it with awk:

awk '{gsub(/[^.]/,""); len=length(); if (len>max) {max=len}} END{printf("Largest count of \".\": %d\n",max)}' file.txt

This will, for every line, replace all characters that are not ., by "nothing" (i.e. remove everything that is not a .). Then, it will count the length of the remaining string, and store the largest value found in max. At end-of-file, it will print the result.

AdminBee
  • 22,803
3

Alternatively, you can count the number of a specific character, and leave the text unchanged for further processing, such as printing the line itself, or counting another character. gsub returns the number of replacements.

awk '{ nDot = gsub ("[.]", "."); etc .. }'
Paul_Pedant
  • 8,679
3

The awk-less answer:

sed 's/[^.]//g' test.dat | wc -L

In other words, keep only the dots, and use the -L option of wc: -L, --max-line-length: print the maximum display width

xenoid
  • 8,888
2

Let's generate an example,

cat >file <<'X'
this.world.
this
1.2.3.4.5
all.is.done
X

With perl

perl -e 'while (<>) { $x = $n if ($n = ($_ =~ y/.//)) > $x } print "$x\n"' file
4

With awk

awk '{ gsub("[^.]", ""); if ((n = length($0)) > x) { x = n } } END { print x }' file
4

With tr and a non-POSIX extended version of wc

tr -cd '.\n' <file | wc -L
4
Chris Davies
  • 116,213
  • 16
  • 160
  • 287
  • The stderr output format of dd is only specified in the POSIX locale, and even there, all it says is it shall be "%u+%u records out\n", <number of whole output blocks>, <number of partial output blocks> (note that leading blanks are also allowed). GNU dd doesn't appear to be compliant in that regard. – Stéphane Chazelas Jul 23 '20 at 11:33
  • And DD reports bytes and not characters, so if you to generalize to any character it won't work. Only the awk and the wc -L version will work on characters coded in more than one byte. – xenoid Jul 23 '20 at 13:07
  • Ok. Option removed. Thank you both – Chris Davies Jul 23 '20 at 13:11
  • 1
    The version with tr and wc -L works OK for me (at least with French characters, assuming UTF-8 encoded input file). – xenoid Jul 23 '20 at 13:17
  • 1
    In UTF-8, bytes with a 0 upper bit can only be 1-byte characters, bytes of multi-bytes characters always have a 1 upper bit, so the ASCII for . cannot match a byte of a multi-byte character. – xenoid Jul 23 '20 at 13:21
  • 1
    @xenoid, GNU wc -L reports the display width, not the number of characters. See Get the display width of a string of characters – Stéphane Chazelas Jul 23 '20 at 18:12
1

One way with awk could be as follows. We need to realize that the following equality holds:

  • number of fields = number of delimiters + 1

Note that adding a 0 to the operand in arithmetic comparison, even though not always necessary, is a good practice to inculcate. At least it helps me think about one less thing, for it becomes an auto reflex coding action. Since Awk does not provide separate operators for arithmetic nd string comparisons, hence coercion is needed to help disambiguate a string from a math operand or rather context.

$ awk -F '[.]' '
    NF>m+0 {m=NF}
    END {print --m}
' file
4
$ awk '
    gsub(/[^.]+/, "") &&
    ! index(t, $0) { t = $0 }
    END { print length(t) }
' file
$ perl -lne '
    my $k = tr/.//;
    $k > $m and $m = $k;
    }{ print $m+0;
' file

The GNU sed editor can also be used in conjunction with the binary calculator bc utility. Idea is we keep lines stripped off of all non-dots and the current longest string of pure dots is held in hold. At eof, we transform the dots into an actionable bc code to generate the number of those dots.

$ sed -Ee '
    s/[^.]+//g;G
    /^(.*)..*\n\1$/!ba
    s/\n.*//;h;:a
    $!d;g;s/./1+/g;s/$/0/
'  file | bc -l
0

JAAOV (Just another awk obfuscating variant...)

awk 'gsub(/[^.]/,"") { print | "wc -L" }'
JJoao
  • 12,170
  • 1
  • 23
  • 45