如何在bash中成对减去字段?

如何在bash中成对减去字段?,bash,unix,awk,Bash,Unix,Awk,我有一个大数据集,如下所示: 5 6 5 6 3 5 2 5 3 7 1 6 4 8 1 8 6 9 1 5 2 9 4 5 对于每一行,我想从第二行减去第一个字段,从第四行减去第三个字段,依此类推字段的数量(总是偶数)。然后,我想报告那些与所有对的差异超过某个限制(比如2)的行。我还应该能够报告下一个最佳的行,即一对比较不符合限制,但所有其他对都符合限制的行 从上面的示例中,如果我将限制设置为2,那么输出文件应该包含 最佳线路: 2 5 3 7 1 6 # because (5-2),

我有一个大数据集,如下所示:

5 6 5 6 3 5
2 5 3 7 1 6
4 8 1 8 6 9
1 5 2 9 4 5
对于每一行,我想从第二行减去第一个字段,从第四行减去第三个字段,依此类推字段的数量(总是偶数)。然后,我想报告那些与所有对的差异超过某个限制(比如2)的行。我还应该能够报告下一个最佳的行,即一对比较不符合限制,但所有其他对都符合限制的行

从上面的示例中,如果我将限制设置为2,那么输出文件应该包含 最佳线路:

2 5 3 7 1 6    # because (5-2), (7-3), (6-1) are all > 2
4 8 1 8 6 9    # because (8-4), (8-1), (9-6) are all > 2 
次佳线路

我目前的方法是读取每一行,将每个字段保存为变量,进行减法运算。 但我不知道如何继续下去


谢谢,

在bash中我可能不会这么做。就我个人而言,我会用Python来做这件事,这对于那些小的、快速的、肮脏的脚本来说通常是很好的

如果您的数据位于文本文件中,则可以阅读如何将该数据作为行列表输入Python。然后可以使用for循环来处理每一行:

threshold = 2
results = []
for line in content:
    numbers = [int(n) for n in line.split()] # Split it into a list of numbers
    pairs = zip(numbers[::2],numbers[1::2]) # Pair up the numbers two and two.
    result = [abs(y - x) for (x,y) in pairs] # Subtract the first number in each pair from the second.
    if sum(result) > threshold:
        results.append(numbers)

下面是一个bash方法:

#!/bin/bash

threshold=$1
shift
file="$@"

a=($(cat "$file"))
b=$(( ${#a[@]}/$(cat "$file" | wc -l) ))

for ((r=0; r<${#a[@]}/b; r++)); do
    br=$((b*r))
    for ((c=0; c<b; c+=2)); do

        if [[ $(( ${a[br + c+1]} - ${a[br + c]} )) < $threshold ]]; then
            break; fi

        if [[ $((c+2)) == $b ]]; then
            echo ${a[@]:$br:$b}; fi

    done
done
然后可以轻松重定向此输出:

$ ./script.sh 2 yourFile.txt > output.txt
注意:如果你在每一行之间都有空行,这就不能正常工作……但我相信上面的内容会让你顺利上路

将“最佳”行打印到文件“最佳”,并将“次最佳”行打印到文件“下一个最佳”

awk'
{
失败计数=0
对于(i=1;i“下一个测试”
}
'阈值=2个输入文件
非常简单的东西

  • 一次遍历字段2
  • 如果(下一个字段-当前字段)未超过
    阈值
    ,则增加
    失败计数
  • 如果该行的
    fail\u count
    为零,则表示它属于“最佳”行

    否则,如果该行的
    fail\u count
    为一,则它属于“次佳”行


  • 另一个bash版本:

    首先是一个只返回结果代码的
    检查函数

    function getLimit() {
        local pairs=0 count=0 limit=$1 wantdiff=$2
        shift 2
        while [ "$1" ] ;do
            [ $(( $2-$1 )) -ge $limit ] && : $((count++))
            : $((pairs++))
            shift 2
          done
        test $((pairs-count)) -eq $wantdiff
    }
    
    比现在:

    while read line ;do getLimit 2 0 $line && echo $line;done <file
    2 5 3 7 1 6
    4 8 1 8 6 9
    

    读取行时;如果可以使用
    awk,则执行getLimit 2 0$line和&echo$line;完成

    $ cat del1
    5 6 5 6 3 5
    2 5 3 7 1 6
    4 8 1 8 6 9
    1 5 2 9 4 5
    1 5 2 9 4 5 3 9
    
    $ cat del1 | awk '{
    > printf "%s _ ",$0; 
    > for(i=1; i<=NF; i+=2){
    >     printf "%d ",($(i+1)-$i)}; 
    >     print NF 
    > }' | awk '{
    > upper=0; 
    > for(i=1; i<=($NF/2); i++){ 
    >     if($(NF-i)>threshold) upper++
    > }; 
    > printf "%d _ %s\n", upper, $0}' threshold=2 | sort -nr
    3 _ 4 8 1 8 6 9 _ 4 7 3 6
    3 _ 2 5 3 7 1 6 _ 3 4 5 6
    3 _ 1 5 2 9 4 5 3 9 _ 4 7 1 6 8
    2 _ 1 5 2 9 4 5 _ 4 7 1 6
    0 _ 5 6 5 6 3 5 _ 1 1 2 6
    
    $cat del1
    5 6 5 6 3 5
    2 5 3 7 1 6
    4 8 1 8 6 9
    1 5 2 9 4 5
    1 5 2 9 4 5 3 9
    $cat del1 | awk'{
    >printf“%s”,$0;
    >对于(i=1;i printf“%d”,($(i+1)-$i)};
    >打印NF
    >}“awk”{
    >上限=0;
    >对于(i=1;i如果($(NF-i)>阈值)上限++
    > }; 
    >printf“%d_%s\n”,上限$0}阈值=2 |排序-nr
    3 _ 4 8 1 8 6 9 _ 4 7 3 6
    3 _ 2 5 3 7 1 6 _ 3 4 5 6
    3 _ 1 5 2 9 4 5 3 9 _ 4 7 1 6 8
    2 _ 1 5 2 9 4 5 _ 4 7 1 6
    0 _ 5 6 5 6 3 5 _ 1 1 2 6
    

    您可以根据需要进一步处理结果。结果按“优”顺序排序。

    您说字段数总是偶数,但示例中的字段数是奇数。您能否详细说明“我还应该能够报告次优行”?您能提供一个有代表性的输入和输出吗?请编辑您的问题,包括可行的示例输入和预期的示例输出,以及您尝试过的任何代码和错误消息。祝您好运。感谢所有的输入,我已经做了必要的更改。非常感谢!但是,我希望在bash中使用它,以便将其集成到现有的script.BTW,它在哪里写输出?对不起,我不熟悉python。我可以换个地方来获得下一个最好的行吗?@asurarocks,你应该可以选择他的答案作为正确的答案。我想我做错了什么。我无法从这个函数中获得任何输出(提示符只会闪烁,直到我输入Ctrl+c)。我在.bashrc文件中包含了getLimit,重新启动了终端并键入了上述命令。@asurarocks:很抱歉,我在复制我的解决方案时忘记了
    shift 2
    。是的,它现在可以工作了!非常优雅的方法,但awk快得多!非常感谢您的回答。您好,doubleDown,我尝试了此解决方案,但它返回了以下错误。awk:compare\u f2.sh:2:awk'awk:compare\u f2.sh:2:^ expressionBeats中的无效字符“”“我被打败了。我将代码复制到一个脚本中,针对示例输入运行,效果很好。搜索错误消息可能会帮助您调试此错误。抱歉,效果很好。我犯了一些愚蠢的错误,仅此而已。我已经投票并选择了我认为这是最好的答案。非常感谢!
    function getLimit() {
        local pairs=0 count=0 limit=$1 wantdiff=$2
        shift 2
        while [ "$1" ] ;do
            [ $(( $2-$1 )) -ge $limit ] && : $((count++))
            : $((pairs++))
            shift 2
          done
        test $((pairs-count)) -eq $wantdiff
    }
    
    while read line ;do getLimit 2 0 $line && echo $line;done <file
    2 5 3 7 1 6
    4 8 1 8 6 9
    
    while read line ;do getLimit 2 1 $line && echo $line;done <file
    1 5 2 9 4 5
    
    $ cat del1
    5 6 5 6 3 5
    2 5 3 7 1 6
    4 8 1 8 6 9
    1 5 2 9 4 5
    1 5 2 9 4 5 3 9
    
    $ cat del1 | awk '{
    > printf "%s _ ",$0; 
    > for(i=1; i<=NF; i+=2){
    >     printf "%d ",($(i+1)-$i)}; 
    >     print NF 
    > }' | awk '{
    > upper=0; 
    > for(i=1; i<=($NF/2); i++){ 
    >     if($(NF-i)>threshold) upper++
    > }; 
    > printf "%d _ %s\n", upper, $0}' threshold=2 | sort -nr
    3 _ 4 8 1 8 6 9 _ 4 7 3 6
    3 _ 2 5 3 7 1 6 _ 3 4 5 6
    3 _ 1 5 2 9 4 5 3 9 _ 4 7 1 6 8
    2 _ 1 5 2 9 4 5 _ 4 7 1 6
    0 _ 5 6 5 6 3 5 _ 1 1 2 6