特定の基準に基づいて行を並べ替え、他の行をマージします。

特定の基準に基づいて行を並べ替え、他の行をマージします。

私のcli fooの一つの弱点はawk。おそらく、よく書かれたスクリプトを使用すると、次の問題を解決できます。しかし、これが仕事に最適なツールであると確信しており、awk私の人生ではこれを行う正しい方法はわかりません。

次のデータファイル(Ledger)があるとします。

2019/05/31 (MMEX948) Gürmar
    Assets:Cash:Marina                       ₺-28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Assets:Cash:Marina                       ₺-28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Assets:Cash:Marina                        ₺-3,45
    Expenses:Food:Groceries:Basic              ₺3,45
    Assets:Cash:Marina                       ₺-15,00
    Expenses:Food:Groceries:Produce           ₺15,00

2019/06/01 (MMEX932) A101
    Assets:Cash:Caleb                     $-3.00
    Assets:Cash:Marina                    $-2.50
    Expenses:Food:Groceries:Basic          $5.50

2019/06/01 (MMEX931) Şemikler Pazar Yeri
    Assets:Cash:Marina                       ₺-24,00
    Expenses:Food:Groceries:Basic             ₺24,00
    Assets:Cash:Marina                       ₺-31,00
    Expenses:Food:Groceries:Meat              ₺31,00
    Assets:Cash:Marina                       ₺-65,00
    Expenses:Food:Groceries:Produce           ₺65,00

各空行で区切られた段落は取引、各インデントは郵便、各投稿にアカウントそして数量(少なくとも2つのスペースで区切ります)。

私はこのデータで2つのことが起こりたいと思います。同じコマンドでこれが発生しても、ツールによっては1〜2回のパスで実行する方が簡単です。

  1. 負の金額に対するすべての転記は、正の金額の転記後に順番に行われなければなりません。

  2. マイナス金額と重複勘定の転記は統合する必要があります。理想的には、金額を合計することをお勧めしますが、これは通貨形式のため非常に複雑で、金額行のみを再生成できるため、必要ありません。パスごとに1つのユニークなアカウントが統合されていない限り、統合パブリケーションから金額を完全に削除するだけで十分です。

結果は次のようになります。

2019/05/31 (MMEX948) Gürmar
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Basic              ₺3,45
    Expenses:Food:Groceries:Produce           ₺15,00
    Assets:Cash:Marina

2019/06/01 (MMEX932) A101
    Expenses:Food:Groceries:Basic          $5.50
    Assets:Cash:Marina                    $-2.50
    Assets:Cash:Caleb

2019/06/01 (MMEX931) Şemikler Pazar Yeri
    Expenses:Food:Groceries:Basic             ₺24,00
    Expenses:Food:Groceries:Meat              ₺31,00
    Expenses:Food:Groceries:Produce           ₺65,00
    Assets:Cash:Marina

コメントを使用すると、重複する項目を検索するよりも、この操作が少し複雑になります。

  • 最初の取引には2つの異なるアカウントが重複しています。そのうちの1つだけマージして消去する必要があります(両方ともマージすることは可能ですが、一度に1つずつマージしない限り、金額を編集することはできません)。
  • 中間取引では統合することはありませんが、すべてのマイナス取引で金額を盲目的に整理するのは間違いです。マージングがないのでクリアする必要は全くありませんが、できるもしそうなら、扱いやすくなります。

この問題をどのように解決しますかawk?または、Awkが最善の解決策ではない場合は何ですか?ほとんどのスクリプト言語(perl、python、zsh)では、すべてを解析して多次元配列に配置し、正規表現の一致量でソートし、2番目はアカウントのアルファでソートして繰り返し出力します。 、常に最後の金額を削除し、最後の重複項目(存在する場合)のみをマージします。

私はAwkで冗長トランザクションを解析してマージする方法を見つけました。

awk 'NF { if (/^20/) { if (last != $$0) print "\n" $$0; last = $$0 } else { print $$0 } }' |

しかし、今より複雑なawkロジックが私に挑戦されています。

ベストアンサー1

このGNU awkスクリプトは私に適しています。

#! /usr/local/bin/awk -f
BEGIN { FS = "[[:space:]][[:space:]]+" }
function dump() {
    for (acct in post) { # dump unmerged postings of current transaction
        if (post[acct])
            print post[acct];
    }
    if (merged) {   # dump merged posting, if any
        printf "    %s\n", merged
    }
    merged = "";    # clear variables for next round
    delete post;
    txn = "";
}
!NF && txn {        # blank line, end of transaction
    dump();
    print;
    next
} 
END { # end-of-file, print merged postings of last txn
    dump();
}
!txn {  # new transaction
    txn = $0;
    print;
    next
}
{
    acct = $2;
    amt = $3
}
amt ~ /-/ { # negative amounts, keep for later
    if (acct in post) { # duplicate entry
        if (!merged || merged == acct) { # only merge and clear one duplicate account
            post[acct] = "";
            merged = acct;
        }
        else  # tack on to existing record without merging
            post[acct] = post[acct] "\n" $0
    }
    else
        post[acct] = $0
    next
}
1

実行中:

~ ./foo.awk foo
2019/05/31 (MMEX948) Gürmar
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Meat              ₺28,14
    Expenses:Food:Groceries:Basic              ₺3,45
    Expenses:Food:Groceries:Produce           ₺15,00
    Assets:Cash:Marina

2019/06/01 (MMEX932) A101
    Expenses:Food:Groceries:Basic          $5.50
    Assets:Cash:Marina                    $-2.50
    Assets:Cash:Caleb                     $-3.00

2019/06/01 (MMEX931) Şemikler Pazar Yeri
    Expenses:Food:Groceries:Basic             ₺24,00
    Expenses:Food:Groceries:Meat              ₺31,00
    Expenses:Food:Groceries:Produce           ₺65,00
    Assets:Cash:Marina

おすすめ記事