私の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つのユニークなアカウントが統合されていない限り、統合パブリケーションから金額を完全に削除するだけで十分です。
結果は次のようになります。
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
答え2
gensub()、配列の配列、およびsorted_inにGNU awkを使用する:
$ cat tst.awk
BEGIN { RS=""; FS="\n"; localeDecPt="."; PROCINFO["sorted_in"]="@val_num_desc" }
{
delete sum
print $1
denom = gensub(/.*([^0-9.,-]).+$/,"\\1",1,$2)
for (i=2; i<=NF; i++) {
account = gensub(/[[:space:]]+[^[:space:]]+$/,"",1,$i)
amount = gensub(/.*[^0-9.,-](.+)$/,"\\1",1,$i)
inputDecPt = gensub(/[0-9-]+/,"","g",amount)
sum[account] += gensub("["inputDecPt"]",localeDecPt,"g",amount)
}
for (account in sum) {
amount = denom gensub("["localeDecPt"]",inputDecPt,"g",sprintf("%0.2f",sum[account]))
printf "%-*s%*s\n", 40, account, 10, amount
}
print ""
}
。
$ awk -f tst.awk file
2019/05/31 (MMEX948) Gürmar
Expenses:Food:Groceries:Meat ₺56,28
Expenses:Food:Groceries:Produce ₺15,00
Expenses:Food:Groceries:Basic ₺3,45
Assets:Cash:Marina ₺-74,73
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:Produce ₺65,00
Expenses:Food:Groceries:Meat ₺31,00
Expenses:Food:Groceries:Basic ₺24,00
Assets:Cash:Marina ₺-120,00
.
小数点がその地域の小数点でない場合は、localeDecPt="."
任意の小数点に変更してください。入力金額に千単位の区切り文字でカンマが含まれていると、公開したコードは機能しないため、テストする値を含む入力を指定する必要があります。出力フィールドの幅を40と10にハードコードしました。各フィールドの最大幅をかなり簡単に計算して使用したり、タブをOFSとして使用したり、出力をにパイプしたりできますが、column
どちらもそうではありません。必要。
マージするアイテムと重複するアイテムを特定する方法の要件を正直に理解していません。は何ですか?)。だから、次を組み合わせます。すべての重複項目の量を削除し、重複していない項目の量を維持します。これがうまくいかない場合は、質問の要件を明確にしてください。