0

I've got a number (lets say 13) and I want to find where it would belong in an incomplete numbered list. Here is an example list:

1
3
4
5
9
16
17
21

Obviously the 13 would go between the 9 and the 16, but how do I grep for the next highest number given that I have the number 13. i.e. How would I create a grep for 16 given that I have 13?

The reason I'm asking is because I want to use sed or awk to insert a line based on a search parameter, but I can't figure out how to grep to find the location.

Please let me know if I need to clarify as it's somewhat hard for me to explain.

1
  • 1
    The reason I'm asking is because... so why don't you ask the question you really want the answer to (e.g. "How do I insert xyz based on a search match pqr?")? Else there's a danger of falling into the XY Problem trap. It sounds like you want a numeric expression rather than a regular expression. Apr 30, 2015 at 20:26

3 Answers 3

1

Maybe not what you wanted, but as you are actually trying to insert a value, I'd personally append the number to the list and then use sort -n to bring things into the correct order.

Such a command could look like this (if the list is in a file):

echo 13 | cat listfile - | sort -n | sed '/^$/d' | cat - > listfile

(Replace the 13 by the number you want to insert, maybe the variable x, so the command sequence would start with echo $x |)

For explanation, I'll split it up:

  • echo 13 | outputs the number 13 to stdout, which is then piped into the next command
  • cat listfile - | first takes the contents of the listfile, afterwards everything from stdin until an EOF, and then pipes the concatenated result to the next commands' stdin
  • sort -n | takes the piped list and sorts it taking the numeric nature of the input into consideration (as given by the -n switch, also see man sort)
  • sed '/^$/d' | removes any empty lines from the string, and forwards it again
  • cat - > listfile puts the whole thing back into listfile again. Yes, this works as opposed to things like sed 's/a/b/g somefile > somefile', which would empty the file.

If you want to split it up into multiple commands, you can make the thing a bite more legible by always operating on the file:

echo 13 >> listfile
sort -n listfile
sed -i '/^$/d' listfile

This can also come in handy if you need to add multiple entries. Simply append them all and then sort (which may be faster than searching for insertion positions each time).


Above command also works if you are operating on variables. For this, the content of the var has to be present as a string using newline separation so it can be sorted line-wise.

If the values are e.g. space separated, you could use the following command

list=$(echo $list 13 | sed -r 's/\s+/\n/g' | sort -n | sed '/^$/d')

Apart from the variable assignment, the only new command is the first sed, which replaces any contiguous sequence of whitespace characters (including newlines) by newlines to create line-separation. In the variable assignment, the list will be automatically converted back into space-separation.

2
  • Hey thanks man this is awesome! I modified it a bit, since I know that the input does not have any blank lines. So I came up with this: echo 13 | cat listfile - | sort -n > listfile and then I put it in a for loop like this: for i in $(<somefile.txt); do echo $i | cat listfile - | sort -n > listfile; done And it works great if I only have one number in the somefile.txt. But as soon as I create a new line with another number, the listfile will only display the second number from the somefile.txt. Do you know why this might be? It seems like it's something wrong with the for loop
    – Evan
    May 5, 2015 at 5:04
  • It's more efficient to insert in a loop, but to sort afterwards.
    – s3lph
    May 5, 2015 at 6:12
0

AFAIU it is nearly impossible to GUESS unless you know the range of numbers next to the number you got. For example, the next number could be 14, could be 50, could be 1000. If you know the range the next number will be in then you can do it easily:

$ grep -E -m 1 "1[4-9]|2[0-9]|3[0-9]|4[0-9]" file.txt
16

Here i am searching for numbers greater than 13 and the range is 14<= NUM <= 49. -m 1 ensures that grep will stop after finding the first match, in this case the next number after 13 in the file.

EDIT: If you are not sure, you can try this script:

#!/bin/bash
i=14
while :; do
    grep "$i" file.txt 2>/dev/null && break || i=$((i+1))
done

Set i as the number you got plus 1. It will go through the file searching for numbers greater than 13, if found the number is shown and the operation ends otherwise the number will be incremented by one and the search will continue.

3
  • I have no way of knowing the range of numbers. Is it possible to set something like this but with a different command and operators? such as >=, >, <, <=, =, etc, etc?
    – Evan
    Apr 30, 2015 at 20:33
  • Basically how to search for the first number > i where i can be any number
    – Evan
    Apr 30, 2015 at 20:36
  • @Evan: Check my edits
    – heemayl
    Apr 30, 2015 at 20:46
0

If you want use awk to insert something, then use it directly:

awk 'flag { print "I am inserting stuff"; flag = 0} $1 > 13 && !seen {flag = 1; seen = 1} 1'

With your example:

$ awk 'flag { print "I am inserting stuff"; flag = 0} $1 > 13 && !seen {flag = 1; seen = 1} 1' foo
1
3
4
5
9
16
I am inserting stuff
17
21

You must log in to answer this question.

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