특정 기준에 따라 라인을 재정렬하고 다른 라인을 병합합니다.

특정 기준에 따라 라인을 재정렬하고 다른 라인을 병합합니다.

내 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개의 공백으로 구분).

나는 이 데이터로 두 가지 일이 일어나길 원합니다. 동일한 명령에서 이런 일이 발생하더라도 도구에 따라 한두 번의 패스로 수행하는 것이 더 쉬울 수 있습니다.

  1. 음수 금액에 대한 모든 전기는 양수 금액에 대한 전기 이후에 순서대로 이루어져야 합니다.

  2. 마이너스 금액과 중복 계정에 대한 전기는 통합되어야 합니다. 이상적으로는 금액을 합산하는 것이 좋지만 이는 통화 형식으로 인해 매우 복잡하고 금액 행만 다시 생성할 수 있으므로 필요하지 않습니다. 패스당 하나의 고유 계정이 통합되지 않는 한 통합 게시에서 금액을 완전히 제거하는 것으로 충분합니다.

결과는 다음과 같아야 합니다.

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

주석을 사용하면 중복 항목을 검색하는 것보다 이 작업이 약간 더 복잡해집니다.

  • 첫 번째 거래에는 서로 다른 계정이 두 개 중복되어 있습니다.그 중 하나만병합하고 지워야 합니다(둘 다 병합하는 것이 가능하지만 한 번에 하나씩만 병합하지 않으면 금액을 수정할 수 없습니다).
  • 중간 거래에서는 통합할 것이 없지만 모든 마이너스 거래에서 금액을 맹목적으로 정리하는 것은 실수입니다. 머징이 없기 때문에 클리어할 필요는 전혀 없지만,할 수 있다그렇다면 처리하기가 더 쉬울 것입니다.

이 문제를 어떻게 해결합니까 awk? 또는 Awk가 최선의 솔루션이 아니라면 무엇입니까? 대부분의 스크립팅 언어(perl, python, zsh)에서는 모든 것을 구문 분석하고 다차원 배열에 넣은 다음 정규식 일치 금액으로 정렬하고 두 번째는 계정의 알파로 정렬한 다음 반복하여 출력합니다. , 항상 마지막 금액을 제거하고 마지막 중복 항목(있는 경우)만 병합합니다.

요전에 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어느 것도 아닌 것처럼 보이지는 않습니다. 필요한.

병합할 항목과 중복 항목을 식별하는 방법에 대한 귀하의 요구 사항을 솔직히 이해하지 못합니다(예: 첫 번째 거래에서 모든 중복 항목을 병합하지 않는 이유와 두 번째 거래에서 중복되지 않은 계정의 금액을 지우는 이유는 무엇입니까?). 그래서 다음을 결합합니다. 모든 중복 항목의 양을 삭제하고 중복되지 않은 항목의 양을 유지합니다. 이것이 효과가 없다면 질문의 요구 사항을 명확히하십시오.

관련 정보