1

With a file of integers separated by newlines, I would like to search for consecutive integers, then list the amount of integers that are consecutive per uninterrupted sequence, as well as the direction (ascending or descending) that each sequence is going in.

My file looks something like this:

2
3
4
5
1
7
4
5
6
3
2
1

And my desired output would be:

4^
3^
3v

With the first character indicating the amount of numbers that are consecutive, and the second character indicating whether the numbers are ascending or descending. Is there a way to do this in bash?

3

2 Answers 2

1

not in bash but in awk. which is considered fair game in bash/shell script by most. by many. at least by me.

func printrun() {
  if(run > 1) {
    print run""dir
  }
}
NR == 1 {
  #print $1" first"
  prev = $1
  dir = "-"
  run = 1
  next
}
$1 == prev+1 && dir == "v" {
  #print $1" up from down"
  printrun()
  prev = $1
  dir = "^"
  run = 2
  next
}
$1 == prev+1 {
  #print $1" up"
  prev = $1
  dir = "^"
  run++
  next
}
$1 == prev-1 && dir == "^"  {
  #print $1" down from up"
  printrun()
  prev = $1
  dir = "v"
  run = 2
  next
}
$1 == prev-1 {
  #print $1" down"
  prev = $1
  dir = "v"
  run++
  next
}
{
  #print $1" else"
  printrun()
  prev = $1
  dir = "-"
  run = 1
}
END {
  #print "end"
  printrun()
}

i tried to compact the code in variations of if and else if chains but i found this variant to be the most clear and readable.

save in run.awk

run like this

$ awk -f run.awk inputfile

or pipe input

$ commandproducinginput | awk -f run.awk

explanation:

awk roughly works like this: it will read the input line by line. for each line it will execute the code blocks for which the condition is true.

a code block is the thing in curly braces. the condition is the part before the code block.

condition { code block }

BEGIN and END are special conditions which are true before the first line and after the last line respectively. in this code we have no BEGIN. only END.

func is not a condition. instead it is a function declaration to be used later.

the condition of the first block is NR == 1. NR is the number of record which practically means line number. in effect this means the block will be executed on the first line and never again. in this block we init the variables to sane values.

this block and most other blocks end with the next statement. next tells awk to abandon this cycle and load the next line and start the next cycle of looking for which blocks to run. with the next in place we effectively created a big if else if chain.

the condition of the next block is $1 == prev+1 && dir == "v". this tests if the current number is one higher than the previous number and if the direction of the run is currently down. if both are true then we print the ongoing run down and update the variables to start a new run up. if not then this block will not be executed and awk will look for the next block to execute.

the following conditions and blocks are analogous to this one.

the second to last block, the block before the END block, is a block without condition. that means it would have been executed for every line. due to the next statements in the previous blocks this block will only be executed if no previous block was executed. in effect this block is the "else" of the if else if chain.

this block is reached if the number is neither consecutive up nor down. so the code in this block sets the vars to correspond to an end of run.

in pseudo code the code might look like this:

if first line:
  init vars and set run to none
else if now going up but was going down:
  print ongoing run down and start a run up
else if now going up:
  start or continue run up
else if now going down but was going up:
  print ongoing run up and start a run down
else if now going down:
  start or continue run down
else:
  print ongoing run and set run to none
if end of input:
  print ongoing run
0
awk 'function prnt(Xdir){ if (c)print c+1, Xdir; c=0 } 
(pre+1==$0){ prev_dir=dir; dir="▲"; if(prev_dir!=dir) prnt(prev_dir); c++; pre=$0; next }
(pre==$0+1){ prev_dir=dir; dir="▼"; if(prev_dir!=dir) prnt(prev_dir); c++; pre=$0; next }
 c{ prnt(dir) } 
{ pre= $0}
END{ prnt(dir) }' infile

4 ▲
3 ▲
3 ▼

You must log in to answer this question.

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