对于由换行符分隔的整数文件,我想搜索连续的整数,然后列出每个不间断序列的连续整数的数量,以及每个序列进入的方向(升序或降序)。
我的文件看起来像这样:
2
3
4
5
1
7
4
5
6
3
2
1
我想要的输出是:
4^
3^
3v
第一个字符指示连续数字的数量,第二个字符指示数字是升序还是降序。有没有办法在 bash 中做到这一点?
答案1
不是在 bash 中,而是在 awk 中。大多数人认为 bash/shell 脚本中的公平游戏。许多人。至少对我来说是这样。
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()
}
我尝试用 if 和 else if 链的变体来压缩代码,但我发现这个变体是最清晰和可读的。
保存在run.awk
像这样跑
$ awk -f run.awk inputfile
或管道输入
$ commandproducinginput | awk -f run.awk
解释:
awk 的工作原理大致如下:它将逐行读取输入。对于每一行,它将执行条件为真的代码块。
代码块是花括号中的东西。条件是代码块之前的部分。
condition { code block }
BEGIN
和END
是特殊条件,分别在第一行之前和最后一行之后为真。在这段代码中我们没有BEGIN
.仅有的END
。
func
不是一个条件。相反,它是一个稍后使用的函数声明。
第一个块的条件是NR == 1
。NR
是记录数,实际上意味着行号。实际上,这意味着该块将在第一行执行,并且不再执行。在此块中,我们将变量初始化为正常值。
该块和大多数其他块都以该next
语句结尾。next
告诉 awk 放弃这个循环并加载下一行并开始下一个循环寻找要运行的块。通过next
就位,我们有效地创建了一个大的 if else if 链。
下一个块的条件是$1 == prev+1 && dir == "v"
。这测试当前数字是否比前一个数字大一以及运行方向当前是否向下。如果两者都为真,那么我们打印正在进行的向下运行并更新变量以开始新的向上运行。如果不是,则不会执行该块,awk 将查找下一个要执行的块。
以下条件和块与此类似。
倒数第二个块(该END
块之前的块)是无条件的块。这意味着它将针对每一行执行。由于next
前面的块中的语句,只有在没有执行前面的块的情况下才会执行该块。实际上,这个块是 if else if 链的“else”。
如果数字既不连续向上也不连续向下,则到达此块。因此该块中的代码将变量设置为对应于运行结束。
在伪代码中,代码可能如下所示:
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
答案2
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 ▼