Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a file which contains several numbers, as follows.

21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44

How can I find the maximum of these values, which is 63? I wanted to use stats but it seems this command does not exist on my machine and I don't want to install it. How can I approach using perl?

share|improve this question

7 Answers 7

up vote 6 down vote accepted

Using List::Util (part of the standard library since 5.8, otherwise available on CPAN):

perl -MList::Util=max -lane 'print max(@F)'
share|improve this answer
1  
List::Util is standard library. –  cuonglm May 19 at 6:18
    
@Gnouc My knowledge about that is out of date, I've updated. Thanks for letting me know. :-) –  Chris Down May 19 at 6:19
    
Thank you all. Is List::Util available on all Linux machines? –  A2009 May 19 at 6:20
1  
@A2009 That would depend on what you mean by a "Linux machine". If you mean that it should be there on systems conforming to a recent version of the LSB, the answer is yes. –  Chris Down May 19 at 6:22

I am aware this is not perl:

awk '{for(i=1;i<=NF;i++) if($i>maxval) maxval=$i;}; END { print maxval;}' file
share|improve this answer
    
Thank you. Is awk available in all Linux machines? –  A2009 May 19 at 6:01
4  
@A2009 Yes, awk is a POSIX tool (as usual: with a standardized body of the language and incompatible extensions by the different implementations). –  Hauke Laging May 19 at 6:18
4  
A nice feature of this solution is that it runs in O(n) time whereas the solutions that do a sort require O(n * log(n)). +1 –  laindir May 19 at 13:41
1  
It refers to the way the running time increases as the size of the set increases (See Big O notation). So if you multiply the input size by 100, an O(n) operation will take 100 times as long, but an O(n * log(n)) operation will take mumbles times as long. My math is wrong, but it goes up more. So while for small inputs, you really can't say which would be faster, for large enough inputs, the O(n) solution will be faster. –  laindir May 19 at 19:16
    
@laindir Re: "What does it mean? That the performance scales in a more predictable way when the size of the set increases? Or that it's always faster?" Your comment has inspired this question. Thank you! –  Amphiteóth May 25 at 22:08

Other answers are quite nice and Perl / awk is the way to go.

Just for fun, you can also use this:

$ grep -o '[0-9]*' file | sort -rn | head -1
63

Explanation

  • grep -o '[0-9]*' file prints all matches of numbers in the file. Each match will be printed in a different line, as per the -o flag.
  • sort -rn sorts the list numerically and in reverse, so that the first number is the biggest.
  • head -1 prints the first line.

By steps:

$ grep -o '[0-9]*' file
21
12
33
35
21
12
33
44
52
63
14
12
23
34
11
12
13
53
1
12
43
33
44

$ grep -o '[0-9]*' file | sort -rn 
63
53
52
44
44
43
35
34
33
33
33
23
21
21
14
13
12
12
12
12
12
11
1
share|improve this answer
1  
This is a nice approach. Thanks. –  A2009 May 19 at 16:13

Another perl solution:

$ perl -MList::Util=max -anle 'print max(@F)' file 
63

This will work with file contains one line, if you have muliple line in file, and want to find the maximum value over all lines, try:

$ perl -MList::Util=max -alne '$tmp = max @F; $max = $tmp if $max < $tmp; END { print $max }'
share|improve this answer

This works in Ubuntu, but didn't on MacOS:

echo "21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44" | grep -oE "[0-9]*" | sort -nr | head -n 1

First grep for patterns matching a number (add ., if you have decimals), then sort numeric in reverse order and then pick the first result.

share|improve this answer
    
Try this instead: echo "21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44" | tr " " "\n" | sort -nr | head -n 1 –  Velox May 19 at 9:46

Here's a faster Perl approach that does not require sorting:

$ echo '21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44' |
    perl -lane 'map{$m=$_ if $_>$m}@F; print $m'

or, as @Gnouc suggested in the comments:

$ echo '21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44' |
    perl -lane '$m<$_ and $m=$_ for @F; print $m'
share|improve this answer
    
Maybe $m>$_ and $m=$_ for @F. With this, we don't have to create another array for map. –  cuonglm May 19 at 16:15
    
@Gnouc no, that fails. You could do foreach(@F){$m=$_ if $_>$m} though. –  terdon May 19 at 16:23
    
Oh, my mistypo $m<$_ and $m=$_ for @F. –  cuonglm May 19 at 16:30
    
@Gnouc ah, yes, thanks. Added. –  terdon May 19 at 16:34

No need for awk, grep, or perl.

{ 
    for n in 21 12 33 35 21 12 33 44 52 63 14 12 23 34 11 12 13 53 1 12 43 33 44
    do 
        echo $n
    done 
} | sort | tail -n1
share|improve this answer
    
Why not simply tr ' ' '\n' < infile | sort -n | tail -n1 since according to the OP, the numbers are stored in a file? Even if they weren't, you could still use tr like velox suggested. –  Thomas Nyman May 19 at 14:15
    
Good call. For some reason I tend to ignore tr. –  blujay May 19 at 14:19

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.