How can I print the longest number in a string?

I am looking for a method to print the longest number in a string.

E.g.: If I have the string

``````212334123434test233
``````

how can I print

``````212334123434
``````

?

Note: I am looking for the longest continuous sequence of numbers, not for the numerically higher value.

Edit: Thanks for the answers, everyone. The response to this question has been quite overwhelming. I marked @HaukeLaging's post as the accepted answer because it suited my specific case very well but I'd like to point out that all answers are equally valid. It's always great to have several different options to solve a problem.

• What do you want the method to do when there are multiple equally long continuous sequences? Take the first? The last? A random one? – Anthon Apr 29 '14 at 4:37
• @Anthon Huh, I hadn't thought of that. Luckily enough that's not an issue in my specific case. I guess any of the options would be fine. – Glutanimate Apr 29 '14 at 4:46
• Note that the answer you have accepted (and all others so far except one) won't deal with decimal numbers. I don't know if that's a problem for you. – terdon Apr 29 '14 at 10:21
• @terdon: It's not an issue in my specific case because I am dealing with IDs rather than actual numbers but I'd like to thank you for your answer nonetheless! I am sure someone else will find it very useful in the future. – Glutanimate Apr 29 '14 at 10:35
• Would you like the solution to be able to deal with negative numbers? And if so - does the minus sign count towards the length? – Floris Apr 29 '14 at 11:34

``````echo 212334123434test233abc44 |
awk '{gsub("[^0-9]+","\n"); print;}' |
awk '{ if (length(\$0) > max) {max = length(\$0); maxline = \$0} }
END { print maxline }'

212334123434
``````

I believe you can do this with just `grep`, `sort`, and `tail` as well. Here are some example strings.

``````\$ echo <str> | grep -oP "\d+" | sort -n | tail -1
``````

Where `<str>` is our string under question.

Example

``````\$ set -o posix; set | grep "str[0-9]"
str0=212334123434test233
str1=212334123434test233abc44
str2=233test212334123434
str3=a212334123434test233abc44
str4=a91234b212334123434abc
``````

Now if I run these through my `grep ...` command in turn.

``````\$ echo \$str0 | grep -oP "\d+" | sort -n | tail -1
212334123434
\$ echo \$str1 | grep -oP "\d+" | sort -n | tail -1
212334123434
\$ echo \$str2 | grep -oP "\d+" | sort -n | tail -1
212334123434
\$ echo \$str3 | grep -oP "\d+" | sort -n | tail -1
212334123434
\$ echo \$str4 | grep -oP "\d+" | sort -n | tail -1
212334123434
``````

This approach works by selecting all the substrings that are sequences of digits. We then sort this output numerically, `sort -n`, and then grab the last value in the list, using `tail -1`. This will be the longest substring.

You can see how it works by taking the `tail -1` off and rerunning one of the examples:

``````\$ echo \$str4 | grep -oP "\d+" | sort -n
91234
212334123434
``````

The above approach works for every situation I could conceive of except one. @terdon mentioned in chat this scenario which foils the above approach.

• 0000000000001
• 2

So to deal with this you'll need to change tactics slightly. The kernel of the above approach can still be leveraged, however we need to inject the number of characters into the results too. This gives sort the ability to sort the results by number of characters in the strings & their values.

``````\$ for i in \$(echo \$str0 | grep -oP "\d+");do a=\$(echo "\$i" | wc -c); \
echo "\$a \$i"; done | sort -n | tail -1 | cut -d" " -f2
``````

Results:

``````\$ echo \$str0
0000000000001a2test

\$ for i in \$(echo \$str0 | grep -oP "\d+");do a=\$(echo "\$i" | wc -c); \
echo "\$a \$i"; done | sort -n | tail -1 | cut -d" " -f2
0000000000001
``````

You can condense this a bit by making use of Bash's ability to determine a variable's length using `\${#var}`.

``````\$ for i in \$(echo \$str0 | grep -oP "\d+");do echo "\${#i} \$i"; done | \
sort -n | tail -1 | cut -d" " -f2
0000000000001
``````

Using `grep -P

I've opted to use `grep -P ...` above because I, being a Perl developer, like the class syntax of saying all digits like so: `\d+`, instead of `[[:digit:]]\+` or `[0-9]\+`. But for this particular problem it isn't really needed. You could just as easily swapped out the `grep` I've used like so:

``````\$ .... grep -o "[0-9]\+" ....
``````

For example:

``````\$ for i in \$(echo \$str0 | grep -o "[0-9]\+");do echo "\${#i} \$i"; done | \
sort -n | tail -1 | cut -d" " -f2
0000000000001
``````
• Using `\${#i}` to get the string length can save you calling `wc`, if you want to go bash-specific – glenn jackman Apr 29 '14 at 17:04
• @glennjackman - thanks added your improvement to my A 8-) – slm Apr 29 '14 at 17:16
• GNU grep 2.16 (at least) says -P is "highly experimental". You can use `grep -o "[0-9]\+"` instead of `grep -oP "\d+"` – David Conrad Apr 29 '14 at 23:53
• @DavidConrad - added these details to the A as well, thanks! – slm Apr 30 '14 at 0:04

A solution in `perl`:

``````echo 212334123434test233abc44 |
perl -nle 'print ((
map { \$_->[0] }
sort{ \$a->[1] <=> \$b->[1] }
map { [\$_,length] }
split /\D+/, \$_)[-1]
)'
212334123434
``````

References

• Love a nice Schwartzian Transform! – glenn jackman Apr 29 '14 at 17:17

Using python with the string passed on the commandline and assuming you want the first sequence of maximum length:

``````import sys

longest = current = ""
for x in sys.argv[1]:
if current and not x.isdigit():
if len(current) > len(longest):
longest = current
current = ""
else:
current += x
print(longest)
``````
• or tersely `python -c "import re,sys; print max(re.split(r'\D+', sys.argv[1]), key=len)"` – iruvar Apr 29 '14 at 13:22

Here's another Perl approach that can deal with decimals as well as integers:

``````echo "0.212334123434test233" |
perl -lne 'while(/([\d.]+)/g){\$max=\$1 if length(\$1) > length(\$max)} print \$max'
``````

Note that none of the answers so far posted will deal with decimals and since you specify that you want the longest and not the numerically largest number, I assume you actually need decimals.

Explanation

• `perl -lne` : The `-n` means "read the input line by line, and run the script given by `-e` on it". The `-l` adds a newline to each `print` call (and other things not relevant here).
• `while(/([\d.]+)/g)` : iterate through all numbers (`\d` means `[0-9]`, so `[\d.]` will match digits and `.`. If you also want to find negative numbers, add `-`. The parentheses capture the matched string as `\$1` which is used in the next step.
• `\$max=\$1 if length(\$1) > length(\$max)` : If the length of the current match is greater than the longest so far (`\$max`) save the match as `\$max`.
• `print \$max` : print the longest string of numbers found. This will be executed after the while loop finishes, so after all numbers have been found.
• +1 Your regex is a bit too generic, though. It would match IP addresses for example. I propose something like `\D(\d+(?:\.\d+)?)\D` instead. – Joseph R. Apr 29 '14 at 17:25
• Should also work without the `\D` anchors... – Joseph R. Apr 29 '14 at 17:48
• @JosephR. hmm, true, I had not considered consecutive `.` like in IP addresses. – terdon Apr 29 '14 at 21:57

Given

``````str="212334123434test233"
``````

then in bash

``````max=""
(( \${#num} > \${#max} )) && max=\$num
done < <(grep -Eo '[0-9]+' <<< "\$str")
echo \$max
212334123434
``````

A possibly purer bash solution using an array constructed by replacing non-digit characters in the string with whitespace, in place of the grep

``````max=""
declare -a nums="\${str//[^[:digit:]]/ }"
for num in \${nums[@]}; do
(( \${#num} > \${#max} )) && max=\$num
done
echo \$max
``````

Building on the answer from @mikeserv, here is yet another alternative. It extracts the numbers (per mikeserv's method), then sorts them in numerical order and takes the last one. Barring leading zeros, this will give you the largest number (not taking account of sign):

``````echo 1111askdlfm2234 |  printf %s\\n \$(tr -sc 0-9 \ ) | sort -n | tail -1
``````
• This one actually works - mine did not. I had the '\r' on the wrong side! I'm going to delete it. You can also just use the shell like - `set -- \$(echo \$str | tr ... ) ; b=\${#1} ; for d ; do [ \${#d} -gt \$b ] && b=\${#d} n=\$d ; done ; echo \$n` – mikeserv Apr 29 '14 at 17:19
• I deleted my own awful post, and you dealt gently enough with me. Since you're already using `tr` anyway, I'd bear no grudge if you incorporated the above. Probably `sort` is faster, but, then again, it waits for the stream end the same as the `\$(subshell)`. I don't know. In any case, yours is already an excellent answer, but if you feel like adding in the above shell loop feel free is all I'm saying. And by the way - it's possible you could do without `sort` altogether with a little creative handling of `wc -L` and `tee` in stream... I'm done with this question though - I'm embarrassed. – mikeserv Apr 29 '14 at 17:42
• One last thing though - you might as well pull `tr` out of the subshell and be rid of `printf`. Just do `'0-9' '\n'`. – mikeserv Apr 29 '14 at 18:04
• @mikeserv - the good thing about this site is that we learn from each other. Thanks for your help; without your answer I would not even have started on my own... – Floris Apr 29 '14 at 18:42

bash and GNU sort

``````IFS=\$'\0' read -r l _ < <(tr -cs '[:digit:]' '[\0*]' <<<'11abcde1234556ghijk22'| sort -znr)
echo \$l
1234556
``````

Use non-numeric characters to split the string, and find the longest sequence or largest numeric value (for equal-length numbers) with a ternary operator.

``````\$ echo "212334123434test233" | awk -F'[^0-9]+' '{for(i=1;i<=NF;i++){m=length(\$i)>=length(m)||\$i>m?\$i:m}};END{print m}'
212334123434
``````

You can also set awk's record separator (`RS`) to be any non-numeric character string:

``````\$ echo "212334123434test233" \
| awk -v RS='[^0-9]+' '
length(longest) < length(\$0) {longest = \$0};
END{print longest}'
212334123434
``````
• Why not just set `RS = '[^0-9]+'` and use Awk's inherent loop? `echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length(\$0) {longest = \$0};END{print longest}' 212334123434` – user61786 Apr 29 '14 at 6:23
• @awk_FTW you should put that down as an answer too. :) Thanks for showing me the `RS` variable, I must admit this is the first time I'm seeing it. You have more tips to offer for `awk` than I do hahaha! – h.j.k. Apr 29 '14 at 7:01